// 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 do LTV verification (using default callback) in PDF document.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using foxit.common;
using foxit.pdf;
using foxit;
using foxit.common.fxcrt;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography;

namespace ltvCS
{

    // Here, the implementation of TrustedCertStoreCallback is very simple : 
    // trust all input certificate(s) when this callback function is triggered during LTV verification.
    // User can improve the implementation of the callback class TrustedCertStoreCallback or choose not to use TrustedCertStoreCallback.
    class MyTrustedCertStoreCallback : TrustedCertStoreCallback {
        public MyTrustedCertStoreCallback() {
        }

        public override bool IsCertTrusted(byte[] cert) {
            return true;
        }

        public override bool IsCertTrustedRoot(byte[] cert) {
            return true;
        }
    };

    class ltv
    {
        public const string input_path = "../input_files/";
        public const string output_directory = "../output_files/ltv/";

        static foxit.common.DateTime GetLocalDateTime()
        {
            System.DateTimeOffset rime = System.DateTimeOffset.Now;
            foxit.common.DateTime datetime = new foxit.common.DateTime();
            datetime.year = (UInt16)rime.Year;
            datetime.month = (UInt16)rime.Month;
            datetime.day = (ushort)rime.Day;
            datetime.hour = (UInt16)rime.Hour;
            datetime.minute = (UInt16)rime.Minute;
            datetime.second = (UInt16)rime.Second;
            datetime.utc_hour_offset = (short)rime.Offset.Hours;
            datetime.utc_minute_offset = (ushort)rime.Offset.Minutes;
            return datetime;
        }

        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 void PKCS7Signature(string input_pdf_path, string signed_pdf_path)
        {
            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 = "adbe.pkcs7.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 = GetLocalDateTime();
                        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.
                        new_signature.StartSign(cert_path, cert_password, Signature.DigestAlgorithm.e_DigestSHA256,
                                                signed_pdf_path, IntPtr.Zero, null);
                    }
                }

            }
        }

        static void UseLTVVerifier(PDFDoc pdf_doc, bool is_to_add_dss) {
            // Here use default RevocationCallback, so no need to call LTVVerifier::SetRevocationCallback
            using (LTVVerifier ltv_verifier = new LTVVerifier(pdf_doc, true, true, false, LTVVerifier.TimeType.e_SignatureCreationTime))
            {
                // Use implemented TrustedCertStoreCallback to trust some cerificates during LTV verification.
                // Here, the implementation of TrustedCertStoreCallback is very simple : 
                // trust all input certificate(s) when this callback function is triggered during LTV verification.
                // User can improve the implementation of the callback class TrustedCertStoreCallback or choose not to use TrustedCertStoreCallback.
                using (MyTrustedCertStoreCallback my_callback = new MyTrustedCertStoreCallback())
                {
                    ltv_verifier.SetTrustedCertStoreCallback(my_callback);
                    ltv_verifier.SetVerifyMode(LTVVerifier.VerifyMode.e_VerifyModeAcrobat);

                    SignatureVerifyResultArray sig_verify_result_array = ltv_verifier.Verify();
                    for (uint i = 0; i < sig_verify_result_array.GetSize(); i++)
                    {
                        SignatureVerifyResult sig_verify_result = sig_verify_result_array.GetAt(i);
                        string signature_name = sig_verify_result.GetSignatureName();
                        uint sig_state = sig_verify_result.GetSignatureState();
                        SignatureVerifyResult.LTVState ltv_state = sig_verify_result.GetLTVState();
                        string ltv_state_str = "";
                        switch (ltv_state)
                        {
                            case SignatureVerifyResult.LTVState.e_LTVStateInactive:
                                ltv_state_str = "inactive";
                                break;
                            case SignatureVerifyResult.LTVState.e_LTVStateEnable:
                                ltv_state_str = "enabled";
                                break;
                            case SignatureVerifyResult.LTVState.e_LTVStateNotEnable:
                                ltv_state_str = "not enabled";
                                break;
                        }
                        Console.WriteLine(String.Format("Signature name:{0}, signature state: {1}, LTV state: {2}",
                                                        signature_name, TransformSignatureStateToString(sig_state),
                                                        ltv_state_str));
                    }

                    if (is_to_add_dss)
                    {
                        for (uint i = 0; i < sig_verify_result_array.GetSize(); i++)
                        {
                            if ((sig_verify_result_array.GetAt(i).GetSignatureState() & Convert.ToUInt32(Signature.States.e_StateVerifyValid)) > 0)
                                ltv_verifier.AddDSS(sig_verify_result_array.GetAt(i));
                        }
                    }
                }
            }
        }

        static void DoLTV(string signed_pdf_path, string saved_ltv_pdf_path)
        {
            // Use default SignatureCallback for signing a time stamp signature with filter "Adobe.PPKLite" and subfilter "ETSI.RFC3161",
            // so no need to register a custom signature callback.

            TimeStampServerMgr.Initialize();
            string free_server_name = "FreeTSAServer";
            string free_server_url = "http://ca.signfiles.com/TSAServer.aspx";
            using (TimeStampServer timestamp_server = TimeStampServerMgr.AddServer(free_server_name, free_server_url, "", ""))
            {
                TimeStampServerMgr.SetDefaultServer(timestamp_server);

                using (PDFDoc pdf_doc = new PDFDoc(signed_pdf_path))
                {
                    pdf_doc.StartLoad(null, true, null);
                    // Add DSS
                    Console.WriteLine("== Before Add DSS ==");
                    UseLTVVerifier(pdf_doc, true);

                    // Add DTS
                    using (PDFPage pdf_page = pdf_doc.GetPage(0))
                    {
                        // The new time stamp signature will have default filter name "Adobe.PPKLite" and default subfilter name "ETSI.RFC3161".
                        using (Signature timestamp_signature = pdf_page.AddSignature(new RectF(), "", Signature.SignatureType.e_SignatureTypeTimeStamp, true))
                        {
                            byte[] empty_byte = Encoding.ASCII.GetBytes("");
                            Progressive sign_progressive = timestamp_signature.StartSign("", empty_byte, Signature.DigestAlgorithm.e_DigestSHA256,
                                                                                         saved_ltv_pdf_path, IntPtr.Zero, null);
                            if (sign_progressive.GetRateOfProgress() != 100)
                                sign_progressive.Continue();

                            // Check saved file.
                            using (PDFDoc check_pdf_doc = new PDFDoc(saved_ltv_pdf_path))
                            {
                                check_pdf_doc.StartLoad(null, true, null);
                                // Just LTV veify.
                                Console.WriteLine("== After Add DSS ==");
                                UseLTVVerifier(check_pdf_doc, false);

                                TimeStampServerMgr.Release();
                            }
                        }
                    }
                }
            }

        }

        static void Main(String[] args)
        {
            string input_file_name = "AboutFoxit.pdf";
            string input_file = 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
            {
                // Add a PKCS7 signature and sign it.
                string signed_pdf_path = output_directory + input_file_name.Substring(0, input_file_name.Length - 4) + "_signed.pdf";
                PKCS7Signature(input_file, signed_pdf_path);

                // Do LTV
                string saved_ltv_pdf_path = signed_pdf_path.Substring(0, signed_pdf_path.Length - 4) + "_ltv.pdf";
                DoLTV(signed_pdf_path, saved_ltv_pdf_path);
            }
            catch (PDFException e)
            {
                Console.WriteLine(e.Message);
            }
            Library.Release();
        }
    }
}
