// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to add, sign, verify and get PAdES level of
// a PAdES signature in PDF document.

using System;
using System.Text;

using foxit.common;
using foxit.pdf;
using foxit.pdf.objects;
using foxit;
using foxit.pdf.annots;
using foxit.common.fxcrt;
using foxit.pdf.interform;

namespace padesCS
{
    class pades
    {
        public const string input_path = "../input_files/";
        public const string output_directory = "../output_files/pades/";

        static string TransformSignatureStateToString(uint sig_state)
        {
            string state_str = "";
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateUnknown)) == Convert.ToUInt32(Signature.States.e_StateUnknown))
                state_str += "Unknown";
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateNoSignData)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "NoSignData";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateUnsigned)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "Unsigned";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateSigned)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "Signed";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyValid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerfiyValid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyInvalid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyInvalid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyErrorData)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyErrorData";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyNoSupportWay)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyNoSupportWay";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyErrorByteRange)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyErrorByteRange";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyChange)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyChange";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIncredible)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIncredible";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyNoChange)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyNoChange";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueValid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueValid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueUnknown)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueUnknown";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueRevoke)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueRevoke";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueExpire)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueExpire";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueUncheck)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueUncheck";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyIssueCurrent)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyIssueCurrent";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampNone)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampNone";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampDoc)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampDoc";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampValid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampValid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampInvalid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampInvalid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampExpire)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampExpire";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampIssueUnknown)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampIssueUnknown";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampIssueValid)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampIssueValid";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateVerifyTimestampTimeBefore)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "VerifyTimestampTimeBefore";
            }
            if ((sig_state & Convert.ToUInt32(Signature.States.e_StateCertCannotGetVRI)) > 0)
            {
                if (state_str.Length > 0) state_str += "|";
                state_str += "CertCannotGetVRI";
            }
            return state_str;
        }
        
        static string TransformLevel2String(Signature.PAdESLevel level) {
            switch (level) {
                case Signature.PAdESLevel.e_PAdESLevelNotPAdES:
                    return "NotPades";
                case Signature.PAdESLevel.e_PAdESLevelNone:
                    return "NoneLevel";
                case Signature.PAdESLevel.e_PAdESLevelBB:
                    return "LevelB";
                case Signature.PAdESLevel.e_PAdESLevelBT:
                    return "LevelT";
                case Signature.PAdESLevel.e_PAdESLevelBLT:
                    return "LevelLT";
                case Signature.PAdESLevel.e_PAdESLevelBLTA:
                    return "LevelLTA";
                default:
                    return "Unknown level value";
            }
        }
        
        static void PAdESSign(string input_pdf_path, string signed_pdf_path, Signature.PAdESLevel pades_level)
        {
            Console.WriteLine("To add a PAdES signature in {0}", pades_level);
            string cached_signed_pdf_path = signed_pdf_path;
            string real_signed_pdf_path = signed_pdf_path;
            if (pades_level > Signature.PAdESLevel.e_PAdESLevelBT)
            {
                cached_signed_pdf_path = signed_pdf_path.Substring(0, signed_pdf_path.Length - 4) + "_cache.pdf";
            }
            using (PDFDoc pdf_doc = new PDFDoc(input_pdf_path))
            {
                pdf_doc.StartLoad(null, true, null);
                using (PDFPage pdf_page = pdf_doc.GetPage(0))
                {
                    float page_width = pdf_page.GetWidth();
                    float page_height = pdf_page.GetHeight();
                    RectF new_sig_rect = new RectF((page_width / 2 - 50.0f), (page_height / 2 - 50.0f), (page_width / 2 + 50.0f), (page_height / 2 + 50.0f));
                    using (Signature new_signature = pdf_page.AddSignature(new_sig_rect))
                    {
                        string filter = "Adobe.PPKLite";
                        string sub_filter = "ETSI.CAdES.detached";
                        new_signature.SetFilter(filter);
                        new_signature.SetSubFilter(sub_filter);

                        //String new_value;
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameSigner, "Foxit PDF SDK");
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameContactInfo, "support@foxitsoftware.com");
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameDN, "CN=CN,MAIL=MAIL@MAIL.COM");
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameLocation, "Fuzhou, China");
                        String new_value = String.Format("As a sample for subfilter \"{0}\"", sub_filter);
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameReason, new_value);
                        new_signature.SetKeyValue(Signature.KeyName.e_KeyNameText, new_value);
                        foxit.common.DateTime sign_time = foxit.common.DateTime.GetLocalTime();
                        new_signature.SetSignTime(sign_time);
                        // Set appearance flags to decide which content would be used in appearance.
                        int ap_flags = Convert.ToInt32(Signature.APFlags.e_APFlagLabel | Signature.APFlags.e_APFlagSigner |
                                                       Signature.APFlags.e_APFlagReason | Signature.APFlags.e_APFlagDN |
                                                       Signature.APFlags.e_APFlagLocation | Signature.APFlags.e_APFlagText |
                                                       Signature.APFlags.e_APFlagSigningTime);
                        new_signature.SetAppearanceFlags(ap_flags);

                        String cert_path = input_path + "foxit_all.pfx";
                        byte[] cert_password = Encoding.ASCII.GetBytes("123456");
                        // Cert file path will be passed back to application through callback function SignatureCallback::Sign().
                        // In this demo, the cert file path will be used for signing in default signature callback.
                        using (Progressive sign_progressive = new_signature.StartSign(cert_path, cert_password, Signature.DigestAlgorithm.e_DigestSHA256,
                                                                               cached_signed_pdf_path, IntPtr.Zero, null)) {
                            if (sign_progressive.GetRateOfProgress() != 100)
                            {
                                if (Progressive.State.e_Finished != sign_progressive.Continue())
                                {
                                    Console.WriteLine("[Failed] Fail to sign the new CAdES signature.");
                                    return;
                                }
                            }
                            if (pades_level > Signature.PAdESLevel.e_PAdESLevelBT)
                            {
                                using (PDFDoc cache_pdf_doc = new PDFDoc(cached_signed_pdf_path))
                                {
                                    cache_pdf_doc.StartLoad(null, true, null);
                                    // Here, we only simply create an empty DSS object in PDF document, just as a simple exmaple.
                                    // In fact, user should use LTVVerifier to add DSS.
                                    cache_pdf_doc.CreateDSS();

                                    if (pades_level > Signature.PAdESLevel.e_PAdESLevelBLT)
                                    {
                                        using (PDFPage cache_pdf_page = cache_pdf_doc.GetPage(0))
                                        {
                                            using (Signature time_stamp_signature = cache_pdf_page.AddSignature(new RectF(), "", Signature.SignatureType.e_SignatureTypeTimeStamp, true))
                                            {
                                                using (Progressive sign_ts_progressive = time_stamp_signature.StartSign("", null,
                                                    Signature.DigestAlgorithm.e_DigestSHA256, real_signed_pdf_path, IntPtr.Zero, null))
                                                {
                                                    if (sign_ts_progressive.GetRateOfProgress() != 100)
                                                    {
                                                        if (Progressive.State.e_Finished != sign_ts_progressive.Continue())
                                                        {
                                                            Console.WriteLine("[Failed] Fail to sign the new time stamp signature.");
                                                            return;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        cache_pdf_doc.SaveAs(real_signed_pdf_path, Convert.ToInt32(PDFDoc.SaveFlags.e_SaveFlagIncremental));
                                    }
                                }
                            }

                        }
                    }
                }

            }
        }

        static void PAdESVerify(string check_pdf_path, Signature.PAdESLevel expect_pades_level)
        {
            using (PDFDoc check_pdf_doc = new PDFDoc(check_pdf_path))
            {
                check_pdf_doc.StartLoad(null, true, null);
                Console.WriteLine("To verify level of PAdES signature in file{0}", check_pdf_path);

                int sig_count = check_pdf_doc.GetSignatureCount();
                if (0 == sig_count)
                    Console.WriteLine("No signature in current PDF file.");
                bool has_cades_signature = false;
                for (int i = 0; i < check_pdf_doc.GetSignatureCount(); i++)
                {
                    using (Signature temp_sig = check_pdf_doc.GetSignature(i))
                    {
                        if (temp_sig.IsEmpty()) continue;
                        uint sig_org_state = temp_sig.GetState();
                        bool is_true = (sig_org_state & Convert.ToUInt32(Signature.States.e_StateSigned)) > 0;
                        if (!is_true) continue;
                        if (temp_sig.GetSubFilter() == "ETSI.CAdES.detached")
                        {
                            has_cades_signature = true;
                            // Verify PAdES signature.
                            using (Progressive verify_progressive = temp_sig.StartVerify(IntPtr.Zero, null))
                            {
                                if (100 != verify_progressive.GetRateOfProgress())
                                {
                                    if (Progressive.State.e_Finished != verify_progressive.Continue())
                                    {
                                        Console.WriteLine("[Failed] Fail to verify a PAdES signature. Signature index:{0}", i);
                                        continue;
                                    }
                                }
                            }
                            uint sig_state = temp_sig.GetState();
                            Console.WriteLine("Signature index: {0}, a PAdES signature. State after verifying: {1}", i, TransformSignatureStateToString(sig_state));

                            // Get PAdES level.
                            Signature.PAdESLevel actual_level = temp_sig.GetPAdESLevel();
                            Console.WriteLine("Signature index:{0}, PAdES level:{1}, {2}",
                                                i, actual_level,
                                                actual_level == expect_pades_level ? "matching expected level." : "NOT match expected level.");
                        }
                    }
                }
                if (false == has_cades_signature)
                    Console.WriteLine("No PAdES signature in current PDF file.");
            }
        }

        static void Main(String[] args)
        {
            string input_file_name = "AboutFoxit.pdf";
            string input_file_path = input_path + input_file_name;
            System.IO.Directory.CreateDirectory(output_directory);

            string sn = "Rew1vB0TqoMIxX444UcpScARWdhgJpsJfzmyMLR32a0+EgrCjp2NaQ==";
            string key = "8f3g1sFttW8NAgfSNv+BvaJWy4Pp8flcm7ncNDkdvCCO4JwUFbGLyZdujReMaiplbYJwwxam3lr7uHtQaEf0xTSu8j2ZBdrZ8FS4OQkEUjOI9UEGb3Ic0GqmlP9T9OnSItOYqoZZA/OnYg5EaVP+c/ha32nUquOk+5riEdhYgINAZDdgJkPc+YSWsrfixOIROf6t34j3eRcyFnOY0MS7tSXi1AEPE0VZdyu+5bfmBSGvLI+WR5lQ+oOQR5hRGwqf990WEdBelNO7dxhZELVhEZ+/L60PpDf5y9A8qmrY6PiEU8nhqIyLpks0BxeIsYyZ3Cr82jjfbItPGyEARsIVzluBSgVMD4oGjojz8T6wGDEXUbmZH/pra94XAs8wZ28mzbmIEppWnhBqX0c6Ov8iXCfFfUZr4KNuPxMEgk4VOYTMOd70GEjGnylivw18hbn7ZpkWh2Zql+uU2HejvOgn+WHvT0HF9NhK0Xb5vH8aFkgBP92PJx8obIbxBVeeExczxvsqWufuPdXS2sPNIgMYZ2TtvqVVsiUToeGB2FQo09fzZOSH5Vi18u/siyvC3FmjeMf4+OYWPuDPNCcrb21NgcHam7XI8AAoGLTGtLLWR+fxynbBBcg2d2ZPcZ5CMFIfOBHRWCkcA0sDT3cl9nsBppHNJze/jVxHy+lREeQEA/etSIgohTGAWW3ZlpibRXqMxlmVWEPifCubbWTzQ6U54mRMpW2UghH3gvCNFNgdday3Av7EtlIJOdUuiMq3zmmd5J4Jt4C82CKnVewA0DtZ4EEtwRG9IYuLrfpuZlnjBAtoeXRV1lqUZ6BcCe0E3m9jO28sGkALuIeJQ74XG7X8lMC7lMWWQYiYD7D6G9QT6X3qV4vXJwNKRKpYc/rI+amEawTSnDYAMEaYPwtZ0rb/7BL+atsZZaI+ncebdfVfjDJZbCZAc+JnkuNlUn2xrNRuObw8h/G0lm2pNOEJ10S8R4+yplo5B1QmC2dCDhaTaW67QJPqnJPKbVmjw7i6KGBi+CDP3JnAKlOX/YUu8uk56JfeQUuGcQg/KaBMGctnchH2n1i3+oGbibhurQj22+OO66Q6mijKoCRP/QJm5WTGcO4c0iVX0B8dIlai/uv/pGU8Wps9MxysH5ViVIj56LWKuNfy1FNO9+wx4iHYnUxfW0Iccq3YJYAlOBkMv8VZIJl9aTEEFJ626LJEw/UbEBdR9CdlwnylPf4RuNW/lcGYMgq3xNVpp/bzYKwZivvQOFgMdX1tv8D33SHvAm3zrRCU+v94BJXnBi9k25fnKsc705IMwv+I6bueXA+mDnagj1oTqsWXZ0WnPAYI3WUO7sBf7XUH230wfYXnTSVhujJxiEP/2oWqi2813gtQkKwz9N2p6yT8Vsh3+BaAwwDvtOZMJKUFcabTBSL6NoouzmzwItJ4mIboT6KXaef66rs8jCU=";
            // Initialize library
            ErrorCode error_code = Library.Initialize(sn, key);
            if (error_code != ErrorCode.e_ErrSuccess)
                Console.WriteLine("Library Initialize Error: {0}\n", error_code);

            try
            {
                TimeStampServerMgr.Initialize();

                Signature.PAdESLevel level = Signature.PAdESLevel.e_PAdESLevelBB;
                string signed_pdf_path = output_directory + input_file_name.Substring(0, input_file_name.Length - 4) + TransformLevel2String(level) + ".pdf";
                PAdESSign(input_file_path, signed_pdf_path, level);
                PAdESVerify(signed_pdf_path, level);

                string server_name = "FreeTSAServer";
                string server_url = "http://ca.signfiles.com/TSAServer.aspx";
                TimeStampServer timestamp_server = TimeStampServerMgr.AddServer(server_name, server_url, "", "");
                TimeStampServerMgr.SetDefaultServer(timestamp_server);
                for (level = Signature.PAdESLevel.e_PAdESLevelBT; level <= Signature.PAdESLevel.e_PAdESLevelBLTA; level =level + 1)
                {
                    signed_pdf_path = output_directory + input_file_name.Substring(0, input_file_name.Length - 4) + TransformLevel2String(level) + ".pdf";
                    PAdESSign(input_file_path, signed_pdf_path, level);
                    PAdESVerify(signed_pdf_path, level);
                }

                TimeStampServerMgr.Release();
            }
            catch (PDFException e)
            {
                Console.WriteLine(e.Message);
            }
            Library.Release();
        }
    }
}
