Skip to content

长效签名验证(LTV,Long term validation)

自 7.0 版本起,福昕 PDF SDK 引入了长效签名验证 (LTV) 的 API 接口,旨在解决签名过期后的验证难题。LTV 的实现依赖于两个关键要素:

  • 文档安全存储 (DSS, Document Security Store):包含签名验证所需的全部信息。
  • 文档时间戳签名 (DTS, Document Timestamp Signature):一种 time stamp 类型的签名,用于记录文档签署时的准确时间。

为支持 LTV 功能,福昕 PDF SDK 提供了以下组件:

  • 时间戳签名支持
    • 提供添加 time stamp 类型签名的 API。
    • 内置 ETSI.RFC3161 子过滤器的默认签名回调。
  • 时间戳服务器管理
    • TimeStampServerMgrTimeStampServer 类,用于配置和管理时间戳服务器。
    • 默认签名回调 ETSI.RFC3161 将自动使用默认配置的时间戳服务器。
  • LTV 验证器
    • LTVVerifier 类,提供验证签名和向文档添加 DSS 信息的功能。
    • 同时,提供 LTVVerifier 所需的基本默认吊销回调函数 RevocationCallback

长效签名验证

以下示例代码演示如何使用 SDK 默认的 sub filter:ETSI.RFC3161 签名回调及默认的 ·RevocationCallback· 进行长效签名验证。详细的信息,请参阅下载包中 \examples\simple_demo 目录下的 ltv 示例。

c++
#include "include/pdf/fs_pdfdoc.h"
#include "include/pdf/fs_pdfpage.h"
#include "include/pdf/fs_signature.h"
#include "include/pdf/fs_ltvverifier.h"
...

// Initialize time stamp server manager, add and set a default time stamp server, which will be used by default signature callback for time stamp signature.
TimeStampServerMgr::Initialize();
TimeStampServer timestamp_server = TimeStampServerMgr::AddServer(server_name, server_url, server_username, server_password);
TimeStampServerMgr::SetDefaultServer(timestamp_server);

// Assume that "signed_pdf_path" represents a signed PDF document which contains signed signature.
PDFDoc pdf_doc(signed_pdf_path);
pdf_doc.StartLoad();
{
	// Use LTVVerifier to verify and add DSS.
	LTVVerifier ltv_verifier(pdf_doc, true, true, false, LTVVerifier::e_SignatureCreationTime);
	// Set verifying mode which is necessary.
	ltv_verifier.SetVerifyMode(LTVVerifier::e_VerifyModeAcrobat);
	SignatureVerifyResultArray sig_verify_result_array = ltv_verifier.Verify();
	for (size_t i = 0; i < sig_verify_result_array.GetSize(); i++) {
		// ltv state would be e_LTVStateNotEnable here.
		SignatureVerifyResult::LTVState ltv_state =  sig_verify_result_array.GetAt(i).GetLTVState();
		if (sig_verify_result_array.GetAt(i).GetSignatureState() & Signature::e_StateVerifyValid)
			ltv_verifier.AddDSS(sig_verify_result_array.GetAt(i));
	}
}

// Add a time stamp signature as DTS and sign it. "saved_ltv_pdf_path" represents the newly saved signed PDF file.
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".
Signature timestamp_signature = pdf_page.AddSignature(RectF(), L"", Signature::e_SignatureTypeTimeStamp);
Progressive sign_progressive = timestamp_signature.StartSign(L"", L"", Signature::e_DigestSHA256, saved_ltv_pdf_path);
if (sign_progressive.GetRateOfProgress() != 100)
	sign_progressive.Continue();

// Then use LTVVeirfier to verify the new signed PDF file.
PDFDoc check_pdf_doc(saved_ltv_pdf_path);
check_pdf_doc.StartLoad();
{
	// Use LTVVeirfier to verify.
	LTVVerifier ltv_verifier(pdf_doc, true, true, false, LTVVerifier::e_SignatureCreationTime);
	// Set verifying mode which is necessary.
	ltv_verifier.SetVerifyMode(LTVVerifier::e_VerifyModeAcrobat);
	SignatureVerifyResultArray sig_verify_result_array = ltv_verifier.Verify();
	for (size_t i = 0; i < sig_verify_result_array.GetSize(); i++) {
		// ltv state would be e_LTVStateEnable here.
		SignatureVerifyResult::LTVState ltv_state =  sig_verify_result_array.GetAt(i).GetLTVState();
		... // User can get other information from SignatureVerifyResult.
	}
}

// Release time stamp server manager when everything is done.
TimeStampServerMgr::Release();
C
#include "include/fs_basictypes_c.h"
#include "include/fs_pdfdoc_c.h"
#include "include/fs_pdfpage_c.h"
#include "include/fs_signature_c.h"
#include "include/fs_ltvverifier_c.h"
...

// Initialize time stamp server manager, add and set a default time stamp server, which will be used by default signature callback for time stamp signature.
FSDK_TimeStampServerMgr_Initialize();
FS_TIMESTAMPSERVER_HANDLE timestamp_server;
FS_WSTR password;
password.str = NULL;
password.len = 0;
FSDK_TimeStampServerMgr_AddServer(server_name, server_url, L"", &password, &timestamp_server);
FSDK_TimeStampServerMgr_SetDefaultServer0(timestamp_server);

// Assume that "signed_pdf_path" represents a signed PDF document which contains signed signature.
FS_PDFDOC_HANDLE pdf_doc;
FSDK_PDFDoc_Create0(signed_pdf_path, &pdf_doc);
FS_PROGRESSIVE_HANDLE progressive;
FSDK_PDFDoc_StartLoad(pdf_doc, NULL, true, NULL, &progressive);
{
	// Use LTVVerifier to verify and add DSS.
	FS_LTVVERIFIER_HANDLE  ltv_verifier;
	FSDK_LTVVerifier_Create(pdf_doc, true, true, false, e_FSSignatureCreationTime, &ltv_verifier);
	// Set verifying mode which is necessary.
	FSDK_LTVVerifier_SetVerifyMode(ltv_verifier, e_FSVerifyModeAcrobat);
	FS_UINT32 length;
	FSDK_LTVVerifier_Verify(ltv_verifier, NULL, &length);
	FS_SIGNATUREVERIFYRESULT_HANDLE*sig_verify_result_array= malloc( length * sizeof(FS_SIGNATUREVERIFYRESULT_HANDLE);
	FSDK_LTVVerifier_Verify(ltv_verifier, sig_verify_result_array, &length);
	for (size_t i = 0; i < length; i++) {
		// ltv state would be e_LTVStateNotEnable here.
		FS_SIGNATUREVERIFYRESULT_HANDLE sig_verify_result = sig_verify_result_array[i];
		FSLTVState ltv_state;
		FSDK_SignatureVerifyResult_GetLTVState(sig_verify_result, &ltv_state);
		FS_UINT32 sig_state;
		FSDK_SignatureVerifyResult_GetSignatureState(sig_verify_result, &sig_state);
		if (sig_state & e_FSStateVerifyValid)
			FSDK_LTVVerifier_AddDSS(ltv_verifier, sig_verify_result_array[i]);
	}
	free(sig_verify_result_array);
}

// Add a time stamp signature as DTS and sign it. "saved_ltv_pdf_path" represents the newly saved signed PDF file.
FS_PDFPAGE_HANDLE pdf_page;
FSDK_PDFDoc_GetPage(pdf_doc, 0, &pdf_page);;
// The new time stamp signature will have default filter name "Adobe.PPKLite" and default subfilter name "ETSI.RFC3161".
FS_SIGNATURE_HANDLE timestamp_signature;
FSRectF rect;
FSDK_PDFPage_AddSignature1(pdf_page, rect, L"", e_FSSignatureTypeTimeStamp, true, &timestamp_signature);
FS_PROGRESSIVE_HANDLE sign_progressive;
FSDK_Signature_StartSign(timestamp_signature, L"", L"", e_FSDigestSHA256, saved_ltv_pdf_path, NULL, NULL, &sign_progressive);
int rate;
FSDK_Progressive_GetRateOfProgress(sign_progressive, &rate);
if (rate != 100){
	FSState state;
	FSDK_Progressive_Continue(sign_progressive, &state);
}
// Then use LTVVeirfier to verify the new signed PDF file.
FS_PDFDOC_HANDLE check_pdf_doc;
FSDK_PDFDoc_Create0(saved_ltv_pdf_path, &check_pdf_doc);
FS_PROGRESSIVE_HANDLE progressive1;
FSDK_PDFDoc_StartLoad(check_pdf_doc, NULL, true, NULL, &progressive1);
{
	// Use LTVVeirfier to verify.
	FS_LTVVERIFIER_HANDLE ltv_verifier;
	FSDK_LTVVerifier_Create(pdf_doc, true, true, false, e_FSSignatureCreationTime, &ltv_verifier);
	// Set verifying mode which is necessary.
	FSDK_LTVVerifier_SetVerifyMode(ltv_verifier, e_FSVerifyModeAcrobat);
	FS_UINT32 length;
	FSDK_LTVVerifier_Verify(ltv_verifier, NULL, &length);
	FS_SIGNATUREVERIFYRESULT_HANDLE*sig_verify_result_array= malloc (length *  sizeof(FS_SIGNATUREVERIFYRESULT_HANDLE));
	FSDK_LTVVerifier_Verify(ltv_verifier, sig_verify_result_array, &length);
	for (size_t i = 0; i < length; i++) {
		// ltv state would be e_LTVStateEnable here.
		FS_SIGNATUREVERIFYRESULT_HANDLE sig_verify_result = sig_verify_result_array[i];
		FSLTVState ltv_state;
		FSDK_SignatureVerifyResult_GetLTVState(sig_verify_result, &ltv_state);
		... // User can get other information from SignatureVerifyResult.
	}
	free(sig_verify_result_array);
}

// Release time stamp server manager when everything is done.
FSDK_TimeStampServerMgr_Release();
java
// Initialize time stamp server manager, add and set a default time stamp server, which will be used by default signature callback for time stamp signature.
TimeStampServerMgr.initialize();
TimeStampServer timestamp_server = TimeStampServerMgr.addServer(server_name, server_url, server_username, server_password);
TimeStampServerMgr.setDefaultServer(timestamp_server);
		 
// Assume that "signed_pdf_path" represents a signed PDF document which contains signed signature.
PDFDoc pdf_doc = new PDFDoc(signed_pdf_path);
pdf_doc.startLoad(null, false, null);
{
   // Use LTVVerifier to verify and add DSS.
   LTVVerifier ltv_verifier = new LTVVerifier(pdf_doc, true, true, false, LTVVerifier.e_SignatureCreationTime);
   // Set verifying mode which is necessary.
   ltv_verifier.setVerifyMode(LTVVerifier.e_VerifyModeAcrobat);
   SignatureVerifyResultArray sig_verify_result_array = ltv_verifier.verify();
   for (long i = 0; i < sig_verify_result_array.getSize(); i++) {
	// ltv state would be e_LTVStateNotEnable here.
	int ltv_state =  sig_verify_result_array.getAt(i).getLTVState();
	if ((sig_verify_result_array.getAt(i).getSignatureState() & Signature.e_StateVerifyValid) == Signature.e_StateVerifyValid)
	    ltv_verifier.addDSS(sig_verify_result_array.getAt(i));
	}
   }		 
		 
// Add a time stamp signature as DTS and sign it. "saved_ltv_pdf_path" represents the newly saved signed PDF file.
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".
Signature timestamp_signature = pdf_page.addSignature(new RectF(), "", Signature.e_SignatureTypeTimeStamp, true);
Progressive sign_progressive = timestamp_signature.startSign("", empty_str.getBytes(), Signature.e_DigestSHA256, saved_ltv_pdf_path, null, null);
if (sign_progressive.getRateOfProgress() != 100)
   sign_progressive.resume();
		 
// Then use LTVVeirfier to verify the new signed PDF file.
PDFDoc check_pdf_doc = new PDFDoc(saved_ltv_pdf_path);
check_pdf_doc.startLoad(null, false, null);
{
   // Use LTVVeirfier to verify.
   LTVVerifier ltv_verifier = new LTVVerifier(pdf_doc, true, true, false, LTVVerifier.e_SignatureCreationTime);
   // Set verifying mode which is necessary.
   ltv_verifier.setVerifyMode(LTVVerifier.e_VerifyModeAcrobat);
   SignatureVerifyResultArray sig_verify_result_array = ltv_verifier.verify();
   for (long i = 0; i < sig_verify_result_array.getSize(); i++) {
	// ltv state would be e_LTVStateEnable here.
	int ltv_state =  sig_verify_result_array.getAt(i).getLTVState();
	... // User can get other information from SignatureVerifyResult.
   }
}
		 
// Release time stamp server manager when everything is done.
TimeStampServerMgr::Release();
py
import sys
import site

if sys.version_info.major == 2:
    _PYTHON2_ = True
else:
    _PYTHON2_ = False

if _PYTHON2_:
    #replace with the python2 lib path
    site.addsitedir('../../../')
    from FoxitPDFSDKPython2 import *
else:
from FoxitPDFSDKPython3 import *

# Implementation of pdf.SignatureCallback
class SignatureCallbackImpl(SignatureCallback):

    def __init__(self, *args):
        if _PYTHON2_:
            super(SignatureCallbackImpl, self).__init__()
        else:
            super().__init__()
        self.digest_context_ = None
        self.sub_filter_ = args[0]

    def __del__(self):
        self.__disown__()

    def Release(self):
        pass

    def GetTextFromFile(self, *args):
        file_buffer = None
        if self.digest_context_ is None or not self.digest_context_.GetFileReadCallback(
        ):
            return False, None
        file_read = self.digest_context_.GetFileReadCallback()
        val, buffer = file_read.ReadBlock(
            (self.digest_context_.GetByteRangeElement(0),
             self.digest_context_.GetByteRangeElement(1)))
        file_buffer = buffer
        val, buffer = file_read.ReadBlock(
            (self.digest_context_.GetByteRangeElement(2),
             self.digest_context_.GetByteRangeElement(3)))
        file_buffer += buffer
        return True, file_buffer

    def StartCalcDigest(self, *args):
        file = args[0]
        byte_range_array = args[1]
        size_of_array = len(args[1])
        signature = args[2]
        client_data = args[3]
        self.digest_context_ = DigestContext(file, byte_range_array,
                                             size_of_array)
        return self.digest_context_.HashInit()

    def ContinueCalcDigest(self, *args):
        try:
            if self.digest_context_ is None:
                return Progressive.e_Error

            ret, file_buffer = self.GetTextFromFile()
            if not ret:
                return Progressive.e_Error
            self.digest_context_.HashUpdate(file_buffer)
            return Progressive.e_Finished
        except Exception as ex:
            print(ex.GetMessage())
        return Progressive.e_Error

    def GetDigest(self, *args):
        try:
            if self.digest_context_ is None:
                return ""
            digest = self.digest_context_.HashDigest()
            return digest
        except Exception as ex:
            print(ex.GetMessage())
        return ""

    def Sign(self, *args):
        try:
            digest = args[0][0]
            digest_length = args[0][1]
            cert_path = args[1]
            password = args[2]
            digest_algorithm = args[3]
            client_data = args[4]

            if self.digest_context_ is None:
                return ""
            plain_text = ""
            if "adbe.pkcs7.sha1" == self.sub_filter_:
                plain_text = digest

            pk12 = crypto.load_pkcs12(open(cert_path, 'rb').read(), password)
            pkey = pk12.get_privatekey()
            signcert = pk12.get_certificate()

            PKCS7_NOSIGS = 0x4

            bio_in = crypto._new_mem_buf(plain_text)
            pkcs7 = crypto._lib.PKCS7_sign(signcert._x509, pkey._pkey,
                                           crypto._ffi.NULL, bio_in,
                                           PKCS7_NOSIGS)
            bio_out = crypto._new_mem_buf()
            crypto._lib.i2d_PKCS7_bio(bio_out, pkcs7)
            signed_data = crypto._bio_to_string(bio_out)
            return signed_data
        except Exception as ex:
            print(ex.GetMessage())

        return ""

    def VerifySigState(self, *args):
        # Usually, the content of a signature field is contain the certification of signer.
        # But we can't judge this certification is trusted.
        # For this example, the signer is ourself. So when using api PKCS7_verify to verify,
        # we pass NULL to it's parameter <i>certs</i>.
        # Meanwhile, if application should specify the certificates, we suggest pass flag PKCS7_NOINTERN to
        # api PKCS7_verify.
        if self.digest_context_ is None:
            return Signature.e_StateVerifyErrorData
        plain_text = ""

        digest = args[0][0]
        digest_length = args[0][1]
        signed_data = args[1][0]
        signed_data_len = args[1][1]
        client_data = args[2]

        if "adbe.pkcs7.sha1" != self.sub_filter_:
            return Signature.e_StateUnknown

        p7 = crypto.load_pkcs7_data(crypto.FILETYPE_ASN1, signed_data)
        PKCS7_NOVERIFY = 0x20
        p7bio = crypto._new_mem_buf(digest)
        res = crypto._lib.PKCS7_verify(p7._pkcs7, crypto._ffi.NULL,
                                       crypto._ffi.NULL, p7bio,
                                       crypto._ffi.NULL, PKCS7_NOVERIFY)

        if res:
            return Signature.e_StateVerifyNoChange
        else:
            return Signature.e_StateVerifyChange

    def IsNeedPadData(self, *args):
        return False

    def CheckCertificateValidity(self, *args):
        # User can check the validity of input certificate here.
        # If no need to check, just return e_CertValid.
        return SignatureCallback.e_CertValid
objc
#include "FSPDFObjC.h"

// Initialize time stamp server manager, add and set a default time stamp server, which will be used by default signature callback for time stamp signature.
[FSTimeStampServerMgr initialize];
FSTimeStampServer* timestamp_server = [FSTimeStampServerMgr addServer:server_name server_url:server_url user_name:user_name password:password];
[FSTimeStampServerMgr setDefaultServer:timestamp_server];

// Assume that "signed_pdf_path" represents a signed PDF document which contains signed signature.
FSPDFDoc *pdf_doc = [[FSPDFDoc alloc] initWithPath:signed_pdf_path];
[pdf_doc startLoad:nil is_cache_stream:NO pause:nil];
{
    // Use LTVVerifier to verify and add DSS.
    FSLTVVerifier* ltv_verifier = [[FSLTVVerifier alloc] initWithDocument:pdf_doc is_verify_signature:YES use_expired_tst:YES ignore_doc_info:NO time_type:FSLTVVerifierSignatureCreationTime];
    // Set verifying mode which is necessary.
    [ltv_verifier setVerifyMode:FSLTVVerifierVerifyModeAcrobat];
    FSSignatureVerifyResultArray* sig_verify_result_array = [ltv_verifier verify];
    unsigned long array_size = [sig_verify_result_array getSize];
    for (size_t i = 0; i < array_size; i++) {
        FSSignatureVerifyResult* sig_verify_result = [sig_verify_result_array getAt:i];
        // ltv state would be FSSignatureVerifyResultLTVStateNotEnable here.
        FSSignatureVerifyResultLTVState ltv_state = [sig_verify_result getLTVState];
        if (([sig_verify_result getSignatureState] & FSSignatureStateVerifyValid) == FSSignatureStateVerifyValid)
            [ltv_verifier addDSS:sig_verify_result];
    }
}

// Add a time stamp signature as DTS and sign it. "saved_ltv_pdf_path" represents the newly saved signed PDF file.
FSPDFPage *pdf_page = [pdf_doc getPage:0];
// The new time stamp signature will have default filter name "Adobe.PPKLite" and default subfilter name "ETSI.RFC3161".
FSRectF* empty_rect = [[FSRectF alloc] init];
FSSignature* timestamp_signature = [pdf_page addSignatureWithSignatureType:empty_rect field_name:@""
signature_type:FSSignatureSignatureTypeTimeStamp to_check_permission:YES];
[timestamp_signature startSign:@"" cert_password:@"" digest_algorithm:FSSignatureDigestSHA1 save_path:saved_ltv_pdf_path client_data:nil pause:nil];

// Then use LTVVeirfier to verify the new signed PDF file.
FSPDFDoc *check_pdf_doc = [[FSPDFDoc alloc] initWithPath:saved_ltv_pdf_path];
[check_pdf_doc startLoad:nil is_cache_stream:NO pause:nil];
{
    // Use LTVVerifier to verify
    FSLTVVerifier* ltv_verifier = [[FSLTVVerifier alloc] initWithDocument:pdf_doc is_verify_signature:YES use_expired_tst:YES ignore_doc_info:NO time_type:FSLTVVerifierSignatureCreationTime];
    // Set verifying mode which is necessary.
    [ltv_verifier setVerifyMode:FSLTVVerifierVerifyModeAcrobat];
    FSSignatureVerifyResultArray* sig_verify_result_array = [ltv_verifier verify];
    unsigned long array_size = [sig_verify_result_array getSize];
    for (size_t i = 0; i < array_size; i++) {
        FSSignatureVerifyResult* sig_verify_result = [sig_verify_result_array getAt:i];
        // ltv state would be FSSignatureVerifyResultLTVStateEnable here.
        FSSignatureVerifyResultLTVState ltv_state = [sig_verify_result getLTVState];
        ... // User can get other information from FSSignatureVerifyResult.
    }
}

// Destroy time stamp server manager when everything is done.
[FSTimeStampServerMgr destroy];
js
const FSDK = require("@foxitsoftware/foxit-pdf-sdk-node");

let filter = "Adobe.PPKLite";
let sub_filter = "adbe.pkcs7.detached";
console.log("Use signature callback object for filter \"%s\" and sub-filter \"%s\"", filter, sub_filter);
let pdf_page = pdf_doc.GetPage(0);
// Add a new signature to first page.
let new_signature = AddSiganture(pdf_page, sub_filter);
// Set filter and subfilter for the new signature.
new_signature.SetFilter(filter);
new_signature.SetSubFilter(sub_filter);
let is_signed = new_signature.IsSigned();
let sig_state = new_signature.GetState();
console.log("[Before signing] Signed?:%s\t State:%s",is_signed? "true" : "false",
TransformSignatureStateToString(sig_state));
// Sign the new signature.
let signed_pdf_path = output_directory + "signed_newsignature_default_handler.pdf";

let cert_file_path = input_path + "foxit_all.pfx";
let cert_file_password = "123456";
// Cert file path will be passed back to application through callback function FSSignatureCallback::Sign().
// In this demo, the cert file path will be used for signing in callback function FSSignatureCallback::Sign().
new_signature.StartSign(cert_file_path, cert_file_password,
                            FSDK.Signature.e_DigestSHA1, signed_pdf_path, null, null);
console.log("[Sign] Finished!");
is_signed = new_signature.IsSigned();
sig_state = new_signature.GetState();
console.log("[After signing] Signed?:%s\tState:%s",is_signed? "true" : "false",
TransformSignatureStateToString(sig_state));
  
// Open the signed document and verify the newly added signature (which is the last one).
console.log("Signed PDF file: %s", signed_pdf_path);
let signed_pdf_doc = new FSDK.PDFDoc(signed_pdf_path);
error_code = signed_pdf_doc.Load("");
if (FSDK.e_ErrSuccess !=error_code ) {
    console.log("Fail to open the signed PDF file.");
    return;
}
// Get the last signature which is just added and signed.
let sig_count = signed_pdf_doc.GetSignatureCount();
let signed_signature = signed_pdf_doc.GetSignature(sig_count-1);
// Verify the intergrity of signature.
signed_signature.StartVerify(Buffer.alloc(0), null);
console.log("[Verify] Finished!");
is_signed = signed_signature.IsSigned();
sig_state = signed_signature.GetState();
console.log("[After verifying] Signed?:%s\tState:%s", is_signed? "true" : "false",
TransformSignatureStateToString(sig_state));
csharp
using System;

using foxit.common;
using foxit.pdf;
using foxit;
using foxit.common.fxcrt;

// Initialize time stamp server manager, add and set a default time stamp server, which will be used by default signature callback for time stamp signature.
TimeStampServerMgr.Initialize();
using (TimeStampServer timestamp_server = TimeStampServerMgr.AddServer(server_name, server_url, server_username, server_password))
{
  TimeStampServerMgr.SetDefaultServer(timestamp_server);
  // Assume that "signed_pdf_path" represents a signed PDF document which contains signed signature.
  using (PDFDoc pdf_doc = new PDFDoc(signed_pdf_path))
  {
    pdf_doc.StartLoad(null, true, null);
    // Use LTVVerifier to verify and add DSS.
    using (LTVVerifier ltv_verifier = new LTVVerifier(pdf_doc, true, true, false, LTVVerifier.TimeType.e_SignatureCreationTime))
    {
      // Set verifying mode which is necessary.
      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++)
      {
        // ltv state would be e_LTVStateNotEnable here.
        SignatureVerifyResult.LTVState ltv_state = sig_verify_result_array.GetAt(i).GetLTVState();
        if ((sig_verify_result_array.GetAt(i).GetSignatureState() & Convert.ToUInt32(Signature.States.e_StateVerifyValid)) > 0)
          ltv_verifier.AddDSS(sig_verify_result_array.GetAt(i));
      }
      // Add a time stamp signature as DTS and sign it. "saved_ltv_pdf_path" represents the newly saved signed PDF file.
      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".
        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();
 
        // Then use LTVVeirfier to verify the new signed PDF file.
        using (PDFDoc check_pdf_doc = new PDFDoc(saved_ltv_pdf_path))
        {
          check_pdf_doc.StartLoad(null, true, null);
          // Use LTVVeirfier to verify.
          using (LTVVerifier ltv_verifier = new LTVVerifier(pdf_doc, true, true, false, LTVVerifier.TimeType.e_SignatureCreationTime))
          {
            // Set verifying mode which is necessary.
            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++)
            {
              // ltv state would be e_LTVStateEnable here.
              SignatureVerifyResult.LTVState ltv_state = sig_verify_result_array.GetAt(i).GetLTVState();
              ... // User can get other information from SignatureVerifyResult.
            }           
          }
        }
      }
    }
  }
}
// Release time stamp server manager when everything is done.
TimeStampServerMgr.Release();
go
import (
.“foxit.com/fsdk”
“fmt”     
"golang.org/x/crypto/pkcs12"
"unsafe"
"crypto"
"crypto/sha1"
"hash"
"crypto/x509"
"encoding/pem"
)

# Implementation of pdf.SignatureCallback
type DigestContext struct {
    fileReadCallback   FileReaderCallback
    byteRangeArray     []uint32
    byteRangeArraySize int
    hasher             hash.Hash
    digest          	string
    digestBytes        []byte
}
func (ctx DigestContext) GetFileReadCallback() FileReaderCallback {
    return ctx.fileReadCallback
}
func (ctx DigestContext) GetByteRangeSize() int {
    return ctx.byteRangeArraySize
}
func (ctx DigestContext) GetByteRangeElement(index int) uint32 {
    if ctx.byteRangeArray != nil && index < ctx.byteRangeArraySize {
        return ctx.byteRangeArray[index]
    }
    return 0
}
func (ctx DigestContext) HashInit() bool {
    fmt.Println("HashInit called")
    ctx.hasher = sha1.New()
    if ctx.hasher == nil {
        fmt.Println("Failed to create hasher")
    }
    return ctx.hasher != nil
}
func (ctx DigestContext) HashUpdate(fileBuffer []byte) {
    fmt.Printf("HashUpdate called with buffer length: %d\n", len(fileBuffer))
    if ctx.hasher != nil {
        fmt.Printf("Updating hash with buffer: %s\n", string(fileBuffer))
        ctx.hasher.Write(fileBuffer)
    }
}
func (ctx DigestContext) HashDigest() string {
    if ctx.hasher != nil {
        return string(ctx.hasher.Sum(nil))
    }
    return ""
}
type SignatureCallbackImpl struct {
    digestContext *DigestContext
    subFilter     string
    SignatureCallback
}
func (sc *SignatureCallbackImpl) Release() {
}
func (sc *SignatureCallbackImpl) GetTextFromFile(buffer uintptr) bool {
    if sc.digestContext == nil || sc.digestContext.GetFileReadCallback() == nil {
        return false
    }

    fileRead := sc.digestContext.GetFileReadCallback()

    // Read first block
    start1 := sc.digestContext.GetByteRangeElement(0)
    length1 := sc.digestContext.GetByteRangeElement(1)- start1
    if !fileRead.ReadBlock(buffer, int64(start1), int64(sc.digestContext.GetByteRangeElement(1))) {
        return false
    }
    start2 := sc.digestContext.GetByteRangeElement(2)
    length2 := sc.digestContext.GetByteRangeElement(3)
    if !fileRead.ReadBlock(buffer+uintptr(length1), int64(start2), int64(length2)) {
        return false
    }
    return true
}
func (sc *SignatureCallbackImpl) StartCalcDigest(file FileReaderCallback, byteRangeArray []uint32, signature Signature, clientData uintptr) bool {
    if sc.digestContext != nil {
        sc.digestContext = nil
    }
    destSlice := make([]uint32, len(byteRangeArray))

    copy(destSlice, byteRangeArray)
    sc.digestContext = &DigestContext{
        fileReadCallback:   file,
        byteRangeArray:     destSlice,
        byteRangeArraySize: len(destSlice),
    }

    sc.digestContext.hasher = sha1.New() 
    return true
}
func (sc *SignatureCallbackImpl) ContinueCalcDigest(clientData uintptr, pause PauseCallback) FoxitCommonProgressive_State {
    if sc.digestContext == nil {
        return ProgressiveE_Error
    }
    fileLength := sc.digestContext.GetByteRangeElement(1) + sc.digestContext.GetByteRangeElement(3)
    fileBuffer := make([]byte, fileLength)
    if fileBuffer == nil || !sc.GetTextFromFile(uintptr(unsafe.Pointer(&fileBuffer[0]))) {
        return ProgressiveE_Error
    }

    sc.digestContext.hasher.Write(fileBuffer)
    return ProgressiveE_Finished
}
func (sc *SignatureCallbackImpl) GetDigest(clientData uintptr) string {
    digest := sc.digestContext.HashDigest()
    return digest
}
func (sc *SignatureCallbackImpl) Sign__SWIG_0(digest uintptr, digestLength uint, certPath string, password string, digestAlgorithm FoxitPdfSignature_DigestAlgorithm, clientData uintptr) string {
    var plainText []byte
    if sc.subFilter == "adbe.pkcs7.sha1" {
        plainText = C.GoBytes(unsafe.Pointer(digest), C.int(digestLength))
    }
    fmt.Printf("certPath: %s, password: %s\n", certPath, password)

    // Load the pfx/p12 file
    pfxData, err := ioutil.ReadFile(certPath)
    if err != nil {
        fmt.Printf("Error reading certificate file: %v\n", err)
        return ""
    }

    // Parse the pfx/p12 file
    privateKey, certificate, err := pkcs12.Decode(pfxData, password)
    if err != nil {
        fmt.Printf("Error decoding PKCS12 data: %v\n", err)
        return ""
    }

    // Create a PKCS7 signature
    pkcs7, err := createPKCS7Signature(plainText, privateKey, certificate)
    if err != nil {
        fmt.Printf("Error creating PKCS7 signature: %v\n", err)
        return ""
    }

    return string(pkcs7)
}

func (sc *SignatureCallbackImpl) Sign__SWIG_1(arg2 uintptr, arg3 uint, arg4 StreamCallback, arg5 string, arg6 FoxitPdfSignature_DigestAlgorithm, arg7 uintptr)  string {
    return ""
}
func createPKCS7Signature(plainText []byte, privateKey crypto.PrivateKey, certificate *x509.Certificate) ([]byte, error) {
    signature, err := privateKey.(crypto.Signer).Sign(nil, plainText, crypto.SHA1)
    if err != nil {
        return nil, fmt.Errorf("error signing data: %v", err)
    }

    certPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "CERTIFICATE",
        Bytes: certificate.Raw,
    })
    result := append(certPEM, signature...)
    return result, nil
}
func (sc *SignatureCallbackImpl) VerifySigState(digest uintptr, digestLength uint, signedData uintptr, signedDataLen uint, clientData uintptr) uint {
    if sc.digestContext == nil {
        return uint(SignatureE_StateVerifyErrorData)
    }
    
    if sc.subFilter != "adbe.pkcs7.sha1" {
        return uint(SignatureE_StateUnknown)
    }
    
    return uint(SignatureE_StateVerifyNoChange)
}
func (sc *SignatureCallbackImpl) IsNeedPadData() bool {
    return false
}
func (sc *SignatureCallbackImpl) CheckCertificateValidity(cert_path string, pw string, cert_data uintptr) FoxitPdfSignatureCallback_CertValidity {
    return SignatureCallbackE_CertValid
}