Skip to content

签名 (Signature)

PDF 签名功能用于创建和签署 PDF 文档的数字签名,旨在保护文档内容的安全性,防止恶意篡改。通过数字签名,接收者可以确信收到的文档来源于签名者,并且文档内容在传输过程中保持完整性,未经任何修改。

福昕 PDF SDK 提供了一系列 API,用于实现以下签名相关操作:

  • 创建数字签名
  • 验证签名的有效性
  • 删除现有的数字签名
  • 获取和设置数字签名的属性
  • 显示签名
  • 自定义签名表单域的外观

默认签名回调函数

福昕 PDF SDK 内置了默认的签名回调函数,支持以下两种标准的签名过滤器 (filter) 和子过滤器 (subfilter):

  1. filter: Adobe.PPKLitesubfilter: adbe.pkcs7.detached
  2. filter: Adobe.PPKLitesubfilter: adbe.pkcs7.sha1

提示:

  • 如果您的签名需求符合以上任一过滤器和子过滤器的组合,您可以直接使用 SDK 提供的 API 进行 PDF 文档的签名和签名有效性验证,无需注册自定义的回调函数,从而简化了开发流程。

对 PDF 文档进行签名

c++
#include "include/pdf/annots/fs_annot.h"
#include "include/common/fs_image.h"
#include "include/pdf/fs_pdfdoc.h"
#include "include/pdf/fs_pdfpage.h"
#include "include/pdf/fs_signature.h"

using namespace foxit;
using namespace foxit::common;
using foxit::common::Library;
using namespace pdf;
using namespace objects;
using namespace file;

// AdobePPKLiteSignature
const char* filter = "Adobe.PPKLite";
const char* sub_filter = "adbe.pkcs7.detached";

if (!use_default) {
	InitializeOpenssl();
	sub_filter = "adbe.pkcs7.sha1";
	SignatureCallbackImpl* sig_callback = new SignatureCallbackImpl(sub_filter);
	Library::RegisterSignatureCallback(filter, sub_filter, sig_callback);
} 

printf("Use signature callback object for filter \"%s\" and sub-filter \"%s\"\r\n",
	  filter, sub_filter);
PDFPage pdf_page = pdf_doc.GetPage(0);
// Add a new signature to the first page.
Signature new_signature = AddSiganture(pdf_page, sub_filter);
// Set filter and subfilter for the new signature.
new_signature.SetFilter(filter);
new_signature.SetSubFilter(sub_filter);
bool is_signed = new_signature.IsSigned();
uint32 sig_state = new_signature.GetState();
printf("[Before signing] Signed?:%s\t State:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state).c_str());

// Sign the new signature.
WString signed_pdf_path = output_directory + L"signed_newsignature.pdf";
if (use_default)
	signed_pdf_path = output_directory + L"signed_newsignature_default_handler.pdf";

WString cert_file_path = input_path + L"foxit_all.pfx";
WString cert_file_password = L"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((const wchar_t*)cert_file_path, cert_file_password,
	  Signature::e_DigestSHA1, (const wchar_t*)signed_pdf_path, NULL, NULL);
printf("[Sign] Finished!\r\n");
is_signed = new_signature.IsSigned();
sig_state = new_signature.GetState();
printf("[After signing] Signed?:%s\tState:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state).c_str());

// Open the signed document and verify the newly added signature (which is the last one).
printf("Signed PDF file: %s\r\n", (const char*)String::FromUnicode(signed_pdf_path));
PDFDoc signed_pdf_doc((const wchar_t*)signed_pdf_path);
ErrorCode error_code = signed_pdf_doc.Load(NULL);
if (foxit::e_ErrSuccess !=error_code ) {
	printf("Fail to open the signed PDF file.\r\n");
	return;
}
// Get the last signature which is just added and signed.
int sig_count = signed_pdf_doc.GetSignatureCount();
Signature signed_signature = signed_pdf_doc.GetSignature(sig_count-1);
// Verify the signature.
signed_signature.StartVerify(NULL, NULL);
printf("[Verify] Finished!\r\n");
is_signed = signed_signature.IsSigned();
sig_state = signed_signature.GetState();
printf("[After verifying] Signed?:%s\tState:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state).c_str());
C
#include "include/fs_basictypes_c.h"
#include "include/fs_annot_c.h"
#include "include/fs_image_c.h"
#include "include/fs_pdfdoc_c.h"
#include "include/fs_pdfpage_c.h"
#include "include/fs_signature_c.h"

// AdobePPKLiteSignature
const char* filter = "Adobe.PPKLite";
const char* sub_filter = "adbe.pkcs7.detached";

if (!use_default) {
	InitializeOpenssl();
	sub_filter = "adbe.pkcs7.sha1";
	FSSignatureCallback* sig_callback = (FSSignatureCallback*)malloc(sizeof(FSSignatureCallback));
               sig_callback->user_data = sig_callback;
               sig_callback->StartCalcDigest = gStartCalcDigest;
               sig_callback->ContinueCalcDigest = gContinueCalcDigest;
               sig_callback->GetDigest = gGetDigest;
               sig_callback->Sign = gSign;
               sig_callback->Sign0 = gSign0;
               sig_callback->VerifySigState = gVerifySigState;
               sig_callback->IsNeedPadData = gIsNeedPadData;
               sig_callback->CheckCertificateValidity = gCheckCertificateValidity;
	FS_BOOL return_Callback;
	FSDK_Library_RegisterSignatureCallback(filter, sub_filter, sig_callback, &return_Callback);
} 

printf("Use signature callback object for filter \"%s\" and sub-filter \"%s\"\r\n",
	  filter, sub_filter);
FS_PDFPAGE_HANDLE pdf_page;
FSDK_PDFDoc_GetPage(pdf_doc, 0, &pdf_page);
// Add a new signature to the first page.
FS_SIGNATURE_HANDLE new_signature = AddSiganture(pdf_page, &sub_filter);
// Set filter and subfilter for the new signature.
FSDK_Signature_SetFilter(new_signature, filter);
FSDK_Signature_SetSubFilter(new_signature, sub_filter);
FS_BOOL is_signed;
FSDK_Signature_IsSigned(new_signature, &is_signed);
FS_UINT32 sig_state;
char sig_state_str[256] = { 0 };
FSDK_Signature_GetState(new_signature, &sig_state);
printf("[Before signing] Signed?:%s\t State:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state_str, 256, sig_state));

// Sign the new signature.
wchar_t signed_pdf_path[MAX_FILE_PATH];
swprintf_s(signed_pdf_path, MAX_FILE_PATH, L"%lssigned_newsignature.pdf",output_directory);
if (use_default)
  swprintf_s(signed_pdf_path, MAX_FILE_PATH, L"%lssigned_newsignature_default_handler.pdf ",output_directory);

wchar_t cert_file_path[MAX_FILE_PATH];
swprintf_s(cert_file_path, MAX_FILE_PATH, L"%lsfoxit_all.pfx", input_path);
const wchar_t* cert_file_password = L"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().
FS_PROGRESSIVE_HANDLE progressive;
FSDK_Signature_StartSign(new_signature, cert_file_path, cert_file_password, e_FSDigestSHA1, signed_pdf_path, NULL, NULL, &progressive);
printf("[Sign] Finished!\r\n");
FSDK_Signature_IsSigned(new_signature, &is_signed);
FSDK_Signature_GetState(new_signature, &sig_state);
printf("[After signing] Signed?:%s\tState:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state_str, 256, sig_state));

// Open the signed document and verify the newly added signature (which is the last one).
wprintf(L"Signed PDF file: %ls\r\n", signed_pdf_path);
FS_PDFDOC_HANDLE signed_pdf_doc;
FSDK_PDFDoc_Create0(signed_pdf_path, &signed_pdf_doc);
FSErrorCode error_code = FSDK_PDFDoc_Load(signed_pdf_doc, NULL);
if (e_FSErrSuccess !=error_code ) {
FSDK_PDFDoc_Release(signed_pdf_doc);
printf("Fail to open the signed PDF file.\r\n");
return;
}
// Get the last signature which is just added and signed.
int sig_count;
FSDK_PDFDoc_GetSignatureCount(signed_pdf_doc, &sig_count);
FS_SIGNATURE_HANDLE signed_signature;
FSDK_PDFDoc_GetSignature(signed_pdf_doc, sig_count - 1, &signed_signature);
// Verify the signature.
FS_PROGRESSIVE_HANDLE progressive2;
FSDK_Signature_StartVerify(signed_signature,NULL ,NULL, &progressive2);
printf("[Verify] Finished!\r\n");
FSDK_Signature_IsSigned(signed_signature, &is_signed);
FSDK_Signature_GetState(signed_signature, &sig_state);
printf("[After verifying] Signed?:%s\tState:%s\r\n",
	  is_signed? "true" : "false",
	  TransformSignatureStateToString(sig_state_str, 256, sig_state));
java
import com.foxit.sdk.pdf.*;
...

String filter = "Adobe.PPKLite";
String sub_filter = "adbe.pkcs7.detached";
PDFPage pdf_page = pdf_doc.getPage(0);
// Add a new signature to first page.
com.foxit.sdk.pdf.Signature new_signature = AddSiganture(pdf_page, sub_filter);
// Set filter and subfilter for the new signature.
new_signature.setFilter(filter);
new_signature.setSubFilter(sub_filter);
boolean is_signed = new_signature.isSigned();
int sig_state = new_signature.getState();
String signed_pdf_path = output_directory + "signed_newsignature.pdf";

String cert_file_path = input_path + "foxit_all.pfx";
String cert_file_password = "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 callback function SignatureCallback::Sign().
new_signature.startSign(cert_file_path, cert_file_password.getBytes(), e_DigestSHA1, signed_pdf_path, null, null);
...
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 *

filter = "Adobe.PPKLite"
sub_filter = "adbe.pkcs7.detached"

if not use_default:
    sub_filter = "adbe.pkcs7.sha1"
    sig_callback = SignatureCallbackImpl(sub_filter)
    Library.RegisterSignatureCallback(filter, sub_filter, sig_callback)

print(
    "Use signature callback object for filter \"{}\" and sub-filter \"{}\"\r\n"
    .format(filter, sub_filter))
pdf_page = pdf_doc.GetPage(0)
# Add a new signature to first page.
new_signature = AddSiganture(pdf_page, sub_filter)
# Set filter and subfilter for the new signature.
new_signature.SetFilter(filter)
new_signature.SetSubFilter(sub_filter)
is_signed = new_signature.IsSigned()
sig_state = new_signature.GetState()
print("[Before signing] Signed?:{}\t State:{}\r\n".format(
    "true" if is_signed else "false",
    TransformSignatureStateToString(sig_state)))

# Sign the new signature.
signed_pdf_path = output_directory + "signed_newsignature.pdf"
if use_default:
    signed_pdf_path = output_directory + "signed_newsignature_default_handle.pdf"

cert_file_path = input_path + "foxit_all.pfx"
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,
                        Signature.e_DigestSHA1, signed_pdf_path)
print("[Sign] Finished!\r\n")
is_signed = new_signature.IsSigned()
sig_state = new_signature.GetState()
print("[After signing] Signed?:{}\tState:{}\r\n".format(
    "true" if is_signed else "false",
    TransformSignatureStateToString(sig_state)))

# Open the signed document and verify the newly added signature (which is the last one).
print("Signed PDF file: {}\r\n".format(signed_pdf_path))
signed_pdf_doc = PDFDoc(signed_pdf_path)
error_code = signed_pdf_doc.Load("")
if e_ErrSuccess != error_code:
    print("Fail to open the signed PDF file.\r\n")
    return

# Get the last signature which is just added and signed.
sig_count = signed_pdf_doc.GetSignatureCount()
signed_signature = signed_pdf_doc.GetSignature(sig_count - 1)
# Verify the intergrity of signature.
signed_signature.StartVerify("", None)
print("[Verify] Finished!\r\n")
is_signed = signed_signature.IsSigned()
sig_state = signed_signature.GetState()
print("[After verifying] Signed?:{}\tState:{}\r\n".format(
    "true" if is_signed else "false",
    TransformSignatureStateToString(sig_state)))
objc
#include "FSPDFObjC.h"
...

NSString *filter = @"Adobe.PPKLite";
NSString *sub_filter = @"adbe.pkcs7.detached";

if (!use_default) {
    InitializeOpenssl();
    sub_filter = @"adbe.pkcs7.sha1";
    SignatureCallback *sig_callback = [[SignatureCallback alloc] initWithSubFilter:sub_filter];
    [FSLibrary registerSignatureCallback:filter sub_filter:sub_filter signature_callback:sig_callback];
}

[filter UTF8String], [sub_filter UTF8String]);
FSPDFPage *pdf_page = [pdf_doc getPage:0];
// Add a new signature to first page.
FSSignature *new_signature = AddSiganture(pdf_page, sub_filter);
// Set filter and subfilter for the new signature.
[new_signature setFilter:filter];
[new_signature setSubFilter:sub_filter];

// Sign the new signature.
NSString *signed_pdf_path = [output_directory stringByAppendingPathComponent:@"signed_newsignature.pdf"];
if (use_default)
    signed_pdf_path = [output_directory stringByAppendingPathComponent:@"signed_newsignature_default_handler.pdf"];

NSString *cert_file_path = [input_path stringByAppendingPathComponent:@"foxit_all.pfx"];
NSString *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_password:cert_file_password digest_algorithm:FSSignatureDigestSHA1 save_path:signed_pdf_path client_data:nil pause:nil];

// Open the signed document and verify the newly added signature (which is the last one).
FSPDFDoc *signed_pdf_doc = [[FSPDFDoc alloc] initWithPath:signed_pdf_path];
FSErrorCode error_code = [signed_pdf_doc load:nil];
if (FSErrSuccess != error_code) {
    return;
}
// Get the last signature which is just added and signed.
int sig_count = [signed_pdf_doc getSignatureCount];
FSSignature *signed_signature = [signed_pdf_doc getSignature:sig_count - 1];
// Verify the signature.
[signed_signature startVerify:nil pause:nil];
...
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 System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using foxit.common;
using foxit.pdf;
using foxit;
using foxit.pdf.annots;
using foxit.common.fxcrt;
using System.Runtime.InteropServices;
using foxit.pdf.interform;

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 Signature AddSiganture(PDFPage pdf_page, string sub_filter) {
    float page_height = pdf_page.GetHeight();
    float page_width = pdf_page.GetWidth();
    RectF new_sig_rect = new RectF(0, (float)(page_height*0.9), (float)(page_width*0.4), page_height);
    // Add a new signature to page.
    Signature new_sig = pdf_page.AddSignature(new_sig_rect);
    if (new_sig.IsEmpty()) return null;
    // Set values for the new signature.
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameSigner, "Foxit PDF SDK");
    String new_value = String.Format("As a sample for subfilter \"{0}\"", sub_filter);
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameReason, new_value);
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameContactInfo, "support@foxitsoftware.com");
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameDN, "CN=CN,MAIL=MAIL@MAIL.COM");
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameLocation, "Fuzhou, China");
    new_value = String.Format("As a sample for subfilter \"{0}\"", sub_filter);
    new_sig.SetKeyValue(Signature.KeyName.e_KeyNameText, new_value);
    foxit.common.DateTime sign_time = GetLocalDateTime();
    new_sig.SetSignTime(sign_time);
    String image_file_path = input_path + "FoxitLogo.jpg";
    using (Image image = new Image(image_file_path))
    {
        new_sig.SetImage(image, 0);
        // 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 | Signature.APFlags.e_APFlagBitmap);
        new_sig.SetAppearanceFlags(ap_flags);
    }

    return new_sig;
}

static void AdobePPKLiteSignature(PDFDoc pdf_doc) {
    string filter = "Adobe.PPKLite";
    string sub_filter = "adbe.pkcs7.detached";

    using (PDFPage pdf_page = pdf_doc.GetPage(0))
    {
        // Add a new signature to first page.
        using (Signature new_signature = AddSiganture(pdf_page, sub_filter))
        {
            // Set filter and subfilter for the new signature.
            new_signature.SetFilter(filter);
            new_signature.SetSubFilter(sub_filter);

            // Sign the new signature.
            String signed_pdf_path = output_directory + "signed_newsignature.pdf";

            String cert_file_path = input_path + "foxit_all.pfx";
            byte[] cert_file_password = Encoding.ASCII.GetBytes("123456");
            new_signature.StartSign(cert_file_path, cert_file_password,
                            Signature.DigestAlgorithm.e_DigestSHA1, signed_pdf_path, IntPtr.Zero, null);
            Console.WriteLine("[Sign] Finished!");
        }
    }
}

static void Main(String[] args)
{
    ...
    AdobePPKLiteSignature(pdf_doc);
    ...
}

实现签名的回调函数

c++
#include "include/pdf/annots/fs_annot.h"
#include "include/common/fs_image.h"
#include "include/pdf/fs_pdfdoc.h"
#include "include/pdf/fs_pdfpage.h"
#include "include/pdf/fs_signature.h"

using namespace foxit;
using namespace foxit::common;
using foxit::common::Library;
using namespace pdf;
using namespace objects;
using namespace file;

// Implementation of pdf::SignatureCallback
class SignatureCallbackImpl : public pdf::SignatureCallback {
public:
	SignatureCallbackImpl(string subfilter)
		: sub_filter_(subfilter)
		, digest_context_(NULL) {}
	~SignatureCallbackImpl();

	virtual void Release() {
		delete this;
	}
	virtual bool StartCalcDigest(const ReaderCallback* file, const uint32* byte_range_array,
		uint32 size_of_array, const Signature& signature, const void* client_data);
	virtual Progressive::State ContinueCalcDigest(const void* client_data, const PauseCallback* pause);
	virtual String GetDigest(const void* client_data);
	virtual String Sign(const void* digest, uint32 digest_length, const wchar_t* cert_path,
		const WString& password, Signature::DigestAlgorithm digest_algorithm,
		void* client_data);
	virtual uint32 VerifySigState(const void* digest, uint32 digest_length,
		const void* signed_data, uint32 signed_data_len,
		void* client_data);
	virtual bool IsNeedPadData() {return false;}
protected:
	bool GetTextFromFile(unsigned char *plainString);

	unsigned char* PKCS7Sign(const wchar_t* cert_file_path, String cert_file_password,
		String plain_text, int& signed_data_size);
	bool PKCS7VerifySignature(String signed_data, String plain_text);
	bool ParseP12File(const wchar_t* cert_file_path, String cert_file_password,
		EVP_PKEY** pkey, X509** x509, STACK_OF(X509)** ca);
	ASN1_INTEGER* CreateNonce(int bits);

private:
	string sub_filter_;
	DigestContext* digest_context_;

	string cert_file_path_;
	string cert_file_password_;
};

SignatureCallbackImpl::~SignatureCallbackImpl() {
	if (digest_context_) {
		delete digest_context_;
		digest_context_ = NULL;
	}
}

bool SignatureCallbackImpl::GetTextFromFile(unsigned char* file_buffer) {
	if (!digest_context_ || !digest_context_->GetFileReadCallback()) return false;
	ReaderCallback* file_read = digest_context_->GetFileReadCallback();
	file_read->ReadBlock(file_buffer, digest_context_->GetByteRangeElement(0), digest_context_->GetByteRangeElement(1));
	file_read->ReadBlock(file_buffer + (digest_context_->GetByteRangeElement(1)-digest_context_->GetByteRangeElement(0)),
		digest_context_->GetByteRangeElement(2), digest_context_->GetByteRangeElement(3));
	return true;
}

bool SignatureCallbackImpl::StartCalcDigest(const ReaderCallback* file, const uint32* byte_range_array,
	uint32 size_of_array, const Signature& signature, const void* client_data) {
		if (digest_context_) {
			delete digest_context_;
			digest_context_ = NULL;
		}
		digest_context_ = new DigestContext(const_cast<ReaderCallback*>(file), byte_range_array, size_of_array);
		if(!SHA1_Init(&digest_context_->sha_ctx_)) {
			delete digest_context_;
			digest_context_ = NULL;
			return false;
		}
		return true;
}

Progressive::State SignatureCallbackImpl::ContinueCalcDigest(const void* client_data, const PauseCallback* pause) {
	if (!digest_context_) return Progressive::e_Error;

	uint32 file_length = digest_context_->GetByteRangeElement(1) + digest_context_->GetByteRangeElement(3);
	unsigned char* file_buffer = (unsigned char*)malloc(file_length);
	if (!file_buffer || !GetTextFromFile(file_buffer)) return Progressive::e_Error;

	SHA1_Update(&digest_context_->sha_ctx_, file_buffer, file_length);
	free(file_buffer);
	return Progressive::e_Finished;
}

String SignatureCallbackImpl::GetDigest(const void* client_data) {
	if (!digest_context_) return "";
	unsigned char* md = reinterpret_cast<unsigned char*>(OPENSSL_malloc((SHA_DIGEST_LENGTH)*sizeof(unsigned char)));
	if (1 != SHA1_Final(md, &digest_context_->sha_ctx_))
		return "";
	String digest = String(reinterpret_cast<const char*>(md), SHA_DIGEST_LENGTH);
	OPENSSL_free(md);
	return digest;
}

String SignatureCallbackImpl::Sign(const void* digest, uint32 digest_length, const wchar_t* cert_path,
	const WString& password, Signature::DigestAlgorithm digest_algorithm,
	void* client_data) {
		if (!digest_context_) return "";
		String plain_text;
		if ("adbe.pkcs7.sha1" == sub_filter_) {
			plain_text = String((const char*)digest, digest_length);
		}
		int signed_data_length = 0;
		unsigned char* signed_data_buffer = PKCS7Sign(cert_path, String::FromUnicode(password),
			plain_text, signed_data_length);
		if (!signed_data_buffer) return "";

		String signed_data = String((const char*)signed_data_buffer, signed_data_length);
		free(signed_data_buffer);
		return signed_data;
}

uint32 SignatureCallbackImpl::VerifySigState(const void* digest, uint32 digest_length,
	const void* signed_data, uint32 signed_data_len, void* client_data) {
		// 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 (!digest_context_) return Signature::e_StateVerifyErrorData;
		String plain_text;
		unsigned char* file_buffer = NULL;
		if ("adbe.pkcs7.sha1" == sub_filter_) {
			plain_text = String(reinterpret_cast<const char*>(digest), digest_length);
		} else {
			return Signature::e_StateUnknown;
		}

		String signed_data_str = String(reinterpret_cast<const char*>(signed_data), signed_data_len);
		bool ret = PKCS7VerifySignature(signed_data_str, plain_text);
		if (file_buffer) free(file_buffer);
		return ret ? Signature::e_StateVerifyNoChange : Signature::e_StateVerifyChange;

}

ASN1_INTEGER* SignatureCallbackImpl::CreateNonce(int bits) {
	unsigned char buf[20];
	int len = (bits - 1) / 8 + 1;
	// Generating random byte sequence.
	if (len > (int)sizeof(buf)) {
		return NULL;
	}
	if (RAND_bytes(buf, len) <= 0) {
		return NULL;
	}
	// Find the first non-zero byte and creating ASN1_INTEGER object.
	int i = 0;
	for (i = 0; i < len && !buf[i]; ++i) ;
	ASN1_INTEGER* nonce = NULL;
	if (!(nonce = ASN1_INTEGER_new())) {
		ASN1_INTEGER_free(nonce);
		return NULL;
	}
	OPENSSL_free(nonce->data);
	// Allocate at least one byte.
	nonce->length = len - i;
	if (!(nonce->data = reinterpret_cast<unsigned char*>(OPENSSL_malloc(nonce->length + 1)))) {
		ASN1_INTEGER_free(nonce);
		return NULL;
	}
	memcpy(nonce->data, buf + i, nonce->length);
	return nonce;
}

bool SignatureCallbackImpl::ParseP12File(const wchar_t* cert_file_path, String cert_file_password,
	EVP_PKEY** pkey, X509** x509, STACK_OF(X509)** ca) {
		FILE* file = NULL;
#if defined(_WIN32) || defined(_WIN64)
		_wfopen_s(&file, cert_file_path, L"rb");
#else
		file = fopen(String::FromUnicode(cert_file_path), "rb");
#endif  // defined(_WIN32) || defined(_WIN64)
		if (!file) {
			return false;
		}

		PKCS12* pkcs12 = d2i_PKCS12_fp(file, NULL);
		fclose (file);
		if (!pkcs12) {
			return false;
		}

		if (!PKCS12_parse(pkcs12, (const char*)cert_file_password, pkey, x509, ca)) {
			return false;
		}

		PKCS12_free(pkcs12);
		if (!pkey)
			return false;
		return true;
}

unsigned char* SignatureCallbackImpl::PKCS7Sign(const wchar_t* cert_file_path, String cert_file_password,
	String plain_text, int& signed_data_size) {
		PKCS7* p7 = NULL;
		EVP_PKEY* pkey = NULL;
		X509* x509 = NULL;
		STACK_OF(X509)* ca = NULL;
		if(!ParseP12File(cert_file_path, cert_file_password, &pkey, &x509, &ca))
			return NULL;

		p7 = PKCS7_new();
		PKCS7_set_type(p7, NID_pkcs7_signed);
		PKCS7_content_new(p7, NID_pkcs7_data);

		// Application should not judge the sign algorithm with the content's length.
		// Here, just for convenient;
		if (plain_text.GetLength() > 32)
			PKCS7_ctrl(p7, PKCS7_OP_SET_DETACHED_SIGNATURE, 1, NULL);

		PKCS7_SIGNER_INFO* signer_info = PKCS7_add_signature(p7, x509, pkey, EVP_sha1());
		PKCS7_add_certificate(p7, x509);

		for (int i = 0; i< sk_num(CHECKED_STACK_OF(X509,ca)); i++)
			PKCS7_add_certificate(p7, (X509*)sk_value(CHECKED_STACK_OF(X509,ca), i));

		// Set source data to BIO.
		BIO* p7bio = PKCS7_dataInit(p7, NULL);
		BIO_write(p7bio, plain_text.GetBuffer(1), plain_text.GetLength());
		PKCS7_dataFinal(p7, p7bio);

		FREE_CERT_KEY;
		BIO_free_all(p7bio);
		// Get signed data.
		unsigned long der_length = i2d_PKCS7(p7, NULL);
		unsigned char* der = reinterpret_cast<unsigned char*>(malloc(der_length));
		memset(der, 0, der_length);
		unsigned char* der_temp = der;
		i2d_PKCS7(p7, &der_temp);
		PKCS7_free(p7);
		signed_data_size = der_length;
		return (unsigned char*)der;
}

bool SignatureCallbackImpl::PKCS7VerifySignature(String signed_data, String plain_text) {
	// Retain PKCS7 object from signed data.
	BIO* vin = BIO_new_mem_buf((void*)signed_data.GetBuffer(1), signed_data.GetLength());
	PKCS7* p7 = d2i_PKCS7_bio(vin, NULL);
	STACK_OF(PKCS7_SIGNER_INFO) *sk = PKCS7_get_signer_info(p7);
	int sign_count = sk_PKCS7_SIGNER_INFO_num(sk);

	int length = 0;
	bool bSigAppr = false;
	unsigned char *p = NULL;
	for(int i=0;i<sign_count; i++) {
		PKCS7_SIGNER_INFO* sign_info = sk_PKCS7_SIGNER_INFO_value(sk,i);

		BIO *p7bio = BIO_new_mem_buf((void*)plain_text.GetBuffer(1), plain_text.GetLength());
		X509 *x509= PKCS7_cert_from_signer_info(p7,sign_info);
		if(1 == PKCS7_verify(p7, NULL, NULL,p7bio, NULL, PKCS7_NOVERIFY))
			bSigAppr = true;
		BIO_free(p7bio);
	}
	PKCS7_free(p7);
	BIO_free(vin);
	return bSigAppr;
}
C
#include "include/fs_basictypes_c.h"
#include "include/fs_annot_c.h"
#include "include/fs_image_c.h"
#include "include/fs_pdfdoc_c.h"
#include "include/fs_pdfpage_c.h"
#include "include/fs_signature_c.h"

// Implementation of pdf::SignatureCallback

// Used for implementing SignatureCallback.
typedef struct _DigestContext
{
  SHA_CTX sha_ctx_;
  FSReaderCallback* file_read_callback_;
  FS_UINT32* byte_range_array_;
  FS_UINT32 byte_range_array_size_;
  FS_BSTR digest_;
  FS_BSTR signed_data;
}DigestContext;

typedef struct _SignatureCallbackData
{
  FS_BSTR sub_filter_;
  DigestContext* digest_context_;
  FS_BSTR cert_file_path_;
  FS_BSTR cert_file_password_;
}SignatureCallbackData;

SignatureCallbackData gsignature_callback_data;

FS_BOOL GetTextFromFile(const DigestContext* digest_context_, unsigned char* file_buffer) {
  FSReaderCallback* file_read;
  FS_UINT32 offset;
  FS_UINT32 size;
  if (!digest_context_ || !digest_context_->file_read_callback_) return FALSE;
  file_read = digest_context_->file_read_callback_;
  offset = digest_context_->byte_range_array_[0];
  size = digest_context_->byte_range_array_[1];
  file_read->ReadBlock(file_read->user_data, file_buffer, digest_context_->byte_range_array_[0], digest_context_->byte_range_array_[1]);
  file_read->ReadBlock(file_read->user_data, file_buffer + (digest_context_->byte_range_array_[1] - digest_context_->byte_range_array_[0]),
    digest_context_->byte_range_array_[2], digest_context_->byte_range_array_[3]);
  return TRUE;
}

FS_BOOL ParseP12File(const wchar_t* cert_file_path, char* cert_file_password,
  EVP_PKEY** pkey, X509** x509, STACK_OF(X509)** ca) {
  FILE* file = NULL;
  PKCS12* pkcs12;
#if defined(_WIN32) || defined(_WIN64)
  _wfopen_s(&file, cert_file_path, L"rb");
#else
  file = fopen(string::FromUnicode(cert_file_path), "rb");
#endif  // defined(_WIN32) || defined(_WIN64)
  if (!file) {
    return FALSE;
  }

  pkcs12 = d2i_PKCS12_fp(file, NULL);
  fclose(file);
  if (!pkcs12) {
    return FALSE;
  }

  if (!PKCS12_parse(pkcs12, cert_file_password, pkey, x509, ca)) {
    return TRUE;
  }

  PKCS12_free(pkcs12);
  if (!pkey)
    return FALSE;
  return TRUE;
}

#define HANDLE_CREATE_TS_ERROR {\
	                         if (!ret)  {\
			 S_REQ_free(ts_req);\
			 ts_req = NULL;\
			  printf("could not create query\n");\
		          }
TS_MSG_IMPRINT_free(msg_imprint);\
	                        X509_ALGOR_free(algo);\
		         ASN1_OBJECT_free(policy_obj);\
		         ASN1_INTEGER_free(nonce_asn1);\
	                         return ts_req;\

ASN1_INTEGER *create_nonce(int bits)
{
  unsigned char buf[20];
  ASN1_INTEGER *nonce = NULL;
  int len = (bits - 1) / 8 + 1;
  int i;

  /* Generating random byte sequence. */
  if (len > (int)sizeof(buf))
  {
    printf("bit count error\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
  }
  if (RAND_bytes(buf, len) <= 0)
  {
    printf("can not generate random number\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
  }

  /* Find the first non-zero byte and creating ASN1_INTEGER object. */
  for (i = 0; i < len && !buf[i]; ++i);
  if (!(nonce = ASN1_INTEGER_new()))
  {
    printf("could not create nonce\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
  }

  OPENSSL_free(nonce->data);
  /* Allocate at least one byte. */
  nonce->length = len - i;
  if (!(nonce->data = (unsigned char *)OPENSSL_malloc(nonce->length + 1)))
  {
    printf("out of memory\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
  }

  memcpy(nonce->data, buf + i, nonce->length);

  return nonce;
}

TS_REQ *create_ts_query(unsigned char *digest, int len)
{
  int ret = 0;
  TS_REQ *ts_req = NULL;
  const EVP_MD *md;
  TS_MSG_IMPRINT *msg_imprint = NULL;
  X509_ALGOR *algo = NULL;
  ASN1_OBJECT *policy_obj = NULL;
  ASN1_INTEGER *nonce_asn1 = NULL;

  switch (len) {
  case 20:
    md = EVP_get_digestbyname("sha1");
    break;
  case 32:
    md = EVP_get_digestbyname("sha256");
    break;
  default:
    HANDLE_CREATE_TS_ERROR;
  }

  /* Creating request object. */
  if (!(ts_req = TS_REQ_new()))
    HANDLE_CREATE_TS_ERROR;

  /* Setting version. */
  if (!TS_REQ_set_version(ts_req, 1))
    HANDLE_CREATE_TS_ERROR;

  /* Creating and adding MSG_IMPRINT object. */
  if (!(msg_imprint = TS_MSG_IMPRINT_new()))
    HANDLE_CREATE_TS_ERROR;

  /* Adding algorithm. */
  if (!(algo = X509_ALGOR_new()))
    HANDLE_CREATE_TS_ERROR;
  if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))))
    HANDLE_CREATE_TS_ERROR;
  if (!(algo->parameter = ASN1_TYPE_new()))
    HANDLE_CREATE_TS_ERROR;
  algo->parameter->type = V_ASN1_NULL;
  if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
    HANDLE_CREATE_TS_ERROR;

  if (!TS_MSG_IMPRINT_set_msg(msg_imprint, digest, len))
    HANDLE_CREATE_TS_ERROR;

  if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
    HANDLE_CREATE_TS_ERROR;

  /* Setting nonce if requested. */
  if (!(nonce_asn1 = create_nonce(64)))
    HANDLE_CREATE_TS_ERROR;
  if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
    HANDLE_CREATE_TS_ERROR;

  /* Setting certificate request flag if requested. */
  if (!TS_REQ_set_cert_req(ts_req, 1))
    HANDLE_CREATE_TS_ERROR;

  ret = 1;
  HANDLE_CREATE_TS_ERROR;
}


long Get_TS_REP(unsigned char *md, int md_len, unsigned char **pRet)
{
  unsigned char *p;
  unsigned char *tsReq;
  int len;
  char SendBuffer[1024] = { 0 };
  const char* bsAuth = ":";
  FS_BSTR bsDestAuth;
  WSADATA  Ws;
  SOCKET CientSocket;
  unsigned char *totalBuf;
  struct sockaddr_in ServerAddr;
  int Ret = 0;
  int AddrLen = 0;
  HOSTENT * iphost;
  char serverIP[20] = "";
  struct servent *pST;
  int sendLen;
  char recvBuf[8096] = { 0 };
  int revtotalLen = 0, recTSL = 0;
  int revLen;
  int nTSRepPos = 0;
  int tsrepL = 0;
  char rspCode[4] = { 0 };
  char *ptr = NULL;

	TS_REQ *request = create_ts_query(md, md_len);
	len = i2d_TS_REQ(request, NULL);
	tsReq = (unsigned char *)OPENSSL_malloc(len);
	p = tsReq;
	len = i2d_TS_REQ(request, (unsigned char **)&p);
	TS_REQ_free(request);

	FSDK_Codec_Base64Encode(bsAuth, 1*sizeof(char), &bsDestAuth);

  sprintf_s(SendBuffer, 1024, "GET %s HTTP/1.1\r\n"
    "Accept: */*\r\n"
    "Content-Type: application/timestamp-query\r\n"
    "Content-Length: %d\r\n"
    "Character-Encoding: binary\r\n"
    "User-Agent: PPKHandler\r\n"
    "Host: %s\r\n"
    "Connection: Keep-Alive\r\n"
    "Cache-Control: no-cache\r\n"
    "Authorization: Basic %s\r\n"
    "\r\n",
    "/TSAServer.aspx", len, "ca.signfiles.com", bsDestAuth.str);

 
	totalBuf = (unsigned char *)malloc(len + strlen(SendBuffer));

	memcpy(totalBuf, SendBuffer, strlen(SendBuffer));
	memcpy(totalBuf + strlen(SendBuffer), tsReq, len);
	OPENSSL_free(tsReq);
	WSAStartup(MAKEWORD(2, 2), &Ws);
	CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if ((iphost = gethostbyname("ca.signfiles.com")) != NULL)
	{
		int i = 0;
		while (iphost->h_addr_list[i])
		{
			memcpy(&serverIP, inet_ntoa(*((struct in_addr *)iphost->h_addr_list[i])), 20);
			i++;
		}
	}

	pST = getservbyname("http", NULL);
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_addr.s_addr = inet_addr(serverIP);
	ServerAddr.sin_port = pST->s_port;
	memset(ServerAddr.sin_zero, 0x00, 8);
	connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
	sendLen = send(CientSocket, (const char *)totalBuf, len + (int)strlen(SendBuffer), 0);

	free(totalBuf);
	revLen = recv(CientSocket, recvBuf, 8096, 0);
	if (revLen == 0)
	{
		printf("\r\ntsa server unreachable.\r\n");
		closesocket(CientSocket);
		WSACleanup();
		return 0;
	}
	revtotalLen += revLen;

	//get the ts reponse length
  strncpy_s(rspCode, 4, recvBuf + 9, 3);
	
	if (strcmp(rspCode, "200") != 0)
	{
		printf("\r\n tsa server refuse request.\r\n%s\r\n", recvBuf);
		closesocket(CientSocket);
		WSACleanup();
		return 0;
	}

  ptr = strstr(recvBuf, "Content-Length") + strlen("Content-Length") + 2;
	//nTSRepPos = (int)(strRecv.find("Content-Length") + strlen("Content-Length") + 2);

	while (*ptr > 47 && *ptr < 58)
	{
		tsrepL = tsrepL * 10 + *ptr - 48;
    ptr += 1;
	}

	while (recTSL < tsrepL && revtotalLen < tsrepL)
	{
		printf("retry, len:%d\n", revLen);
		revLen = recv(CientSocket, recvBuf + revtotalLen, 8096 - revtotalLen, 0);
		recTSL += revLen;
		revtotalLen += revLen;
	}
	*pRet = (unsigned char *)OPENSSL_malloc(tsrepL);
	memcpy(*pRet, recvBuf + revtotalLen - tsrepL, tsrepL);

	closesocket(CientSocket);
	WSACleanup();

	return tsrepL;
}

int append_tsp_token(PKCS7_SIGNER_INFO *sinfo, unsigned char *ts_rep, long tsrepL)
{

	unsigned char *p = ts_rep;
	TS_RESP *tsp = d2i_TS_RESP(NULL, (const unsigned char**)&p, tsrepL);
	if (tsp != NULL)
	{
    int p7_len;
    unsigned char *p7_der = NULL;
    unsigned char *p = NULL;
    PKCS7* token = TS_RESP_get_token(tsp);
		if (!PKCS7_type_is_signed(token))
		{
			printf("Error in timestamp token: not signed!\n");
			return 1;
		}

		p7_len = i2d_PKCS7(token, NULL);
		p7_der = (unsigned char *)OPENSSL_malloc(p7_len);
		p = p7_der;
		i2d_PKCS7(token, &p);

		if (sinfo)
		{
			//Add timestamp token to the PKCS7 signature object
			ASN1_STRING *value = ASN1_STRING_new();
			ASN1_STRING_set(value, p7_der, p7_len);
			PKCS7_add_attribute(sinfo, NID_id_smime_aa_timeStampToken, V_ASN1_SEQUENCE, value);
			OPENSSL_free(p7_der);
		}
	} else {
		printf("Error decoding timestamp token!\n");
		return 1;
	}
	return 0;
}


FS_BOOL gStartCalcDigest(void* user_data, const FSReaderCallback* file, const FS_UINT32* byte_range_array,
  FS_UINT32 size_of_array, const FS_SIGNATURE_HANDLE signature, const void* client_data) {
  if (gsignature_callback_data.digest_context_) {
    free(gsignature_callback_data.digest_context_);
    gsignature_callback_data.digest_context_ = NULL;
  }
  gsignature_callback_data.digest_context_ = (DigestContext*)malloc(sizeof(DigestContext));
  FSDK_BStr_Init(&gsignature_callback_data.digest_context_->signed_data);
  FSDK_BStr_Init(&gsignature_callback_data.digest_context_->digest_);
  gsignature_callback_data.digest_context_->file_read_callback_ = (FSReaderCallback*)file;
  gsignature_callback_data.digest_context_->byte_range_array_ = (FS_UINT32*)byte_range_array;
  gsignature_callback_data.digest_context_->byte_range_array_size_ = size_of_array;

  if (!SHA1_Init(&(gsignature_callback_data.digest_context_->sha_ctx_))) {
    free(gsignature_callback_data.digest_context_);
    gsignature_callback_data.digest_context_ = NULL;
    return FALSE;
  }
  return TRUE;
}


FSState gContinueCalcDigest(void* user_data, const void* client_data, const FSPauseCallback* pause) {
  FS_UINT32 file_length;
  unsigned char* file_buffer;
  if (!gsignature_callback_data.digest_context_) return e_FSError;

  file_length = gsignature_callback_data.digest_context_->byte_range_array_[1] + gsignature_callback_data.digest_context_->byte_range_array_[3];
  file_buffer = (unsigned char*)malloc(file_length);
  if (!file_buffer || !GetTextFromFile(gsignature_callback_data.digest_context_, file_buffer)) return e_FSError;

  SHA1_Update(&(gsignature_callback_data.digest_context_->sha_ctx_), file_buffer, file_length);
  free(file_buffer);
  return e_FSFinished;
}

FS_BSTR gGetDigest(void* user_data, const void* client_data) {
  FS_BSTR digest;
  unsigned char* md;
  if (!gsignature_callback_data.digest_context_) {
    digest.str = NULL;
    digest.len = 0;
    return digest;
  };
  md = (unsigned char*)(OPENSSL_malloc((SHA_DIGEST_LENGTH) * sizeof(unsigned char)));
  if (1 != SHA1_Final(md, &(gsignature_callback_data.digest_context_->sha_ctx_))) {
    digest.str = NULL;
    digest.len = 0;
    return digest;
  }

  if (gsignature_callback_data.digest_context_->digest_.str) {
    FSDK_BStr_Clear(&gsignature_callback_data.digest_context_->digest_);
  }

  FSDK_BStr_SetLength(&gsignature_callback_data.digest_context_->digest_, SHA_DIGEST_LENGTH);
  FSDK_BStr_Set(&gsignature_callback_data.digest_context_->digest_, (const char*)md, SHA_DIGEST_LENGTH);

  OPENSSL_free(md);
  return gsignature_callback_data.digest_context_->digest_;
}

unsigned char* PKCS7Sign(const wchar_t* cert_file_path, char* cert_file_password,
  char* plain_text, int plain_text_size, int* signed_data_size);
FS_BOOL PKCS7VerifySignature(FS_BSTR signed_data, FS_BSTR plain_text);

FS_BSTR gSign(void* user_data, const void* digest, FS_UINT32 digest_length, const wchar_t* cert_path,
  const wchar_t* password, FSDigestAlgorithm digest_algorithm,
  void* client_data) {
  FS_BSTR bstr;
  char* plain_text = NULL;
  int signed_data_length = 0;
  size_t password_size;
  size_t pass_word_size;
  char* pass_word = NULL;
  unsigned char* signed_data_buffer;
  bstr.str = NULL;
  bstr.len = 0;
  if (!gsignature_callback_data.digest_context_) {
    return bstr;
  }

  if(strcmp("adbe.pkcs7.sha1", gsignature_callback_data.sub_filter_.str) == 0)  {  
    plain_text = (char*)malloc(digest_length);
    memcpy(plain_text, digest, digest_length);
  }

  password_size = wcslen(password);
  pass_word_size = password_size + 1;
  pass_word = (char*)malloc(pass_word_size);
  signed_data_buffer = PKCS7Sign(cert_path, wstring2string(password, password_size, pass_word, pass_word_size),
    plain_text, digest_length, &signed_data_length);
  free(pass_word);
  free(plain_text);
  if (!signed_data_buffer) return bstr;
  if (!gsignature_callback_data.digest_context_->signed_data.str) {
    FSDK_BStr_Clear(&gsignature_callback_data.digest_context_->signed_data);
  }
  
  FSDK_BStr_SetLength(&gsignature_callback_data.digest_context_->signed_data, signed_data_length);
  FSDK_BStr_Set(&gsignature_callback_data.digest_context_->signed_data, (char*)(const char*)signed_data_buffer, signed_data_length);
  free(signed_data_buffer);
  return gsignature_callback_data.digest_context_->signed_data;
}

FS_BSTR gSign0(void* user_data, const void* digest, FS_UINT32 digest_length, FSIFX_FileStream* cert_file_stream, const wchar_t* password, FSDigestAlgorithm digest_algorithm, void* client_data) {
  FS_BSTR bstr;
  bstr.str = "";
  bstr.len = 0;
  return bstr;
}

FS_UINT32 gVerifySigState(void* user_data, const void* digest, FS_UINT32 digest_length,const void* signed_data, FS_UINT32 signed_data_len,void* client_data) {
  // 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.
  FS_BSTR plain_text;
  unsigned char* file_buffer = NULL;
  FS_BSTR signed_data_str;
  FS_BOOL ret;
  if (!gsignature_callback_data.digest_context_) return e_FSStateVerifyErrorData;
  if (strcmp("adbe.pkcs7.sha1", gsignature_callback_data.sub_filter_.str) == 0) {
    FSDK_BStr_Init(&plain_text);
    FSDK_BStr_SetLength(&plain_text, digest_length);
    FSDK_BStr_Set(&plain_text, digest, digest_length);
  } else {
    return e_FSStatesStateUnknown;
  }  
  FSDK_BStr_Init(&signed_data_str);
  FSDK_BStr_SetLength(&signed_data_str, signed_data_len);
  FSDK_BStr_Set(&signed_data_str, signed_data, signed_data_len);

  ret = PKCS7VerifySignature(signed_data_str, plain_text);
  if (file_buffer) free(file_buffer);
  FSDK_BStr_Clear(&signed_data_str);
  FSDK_BStr_Clear(&plain_text);
  //this function is only used to verify the intergrity of a signature.
  return ret ? e_FSStateVerifyNoChange : e_FSStateVerifyChange;
}

void gRelease(void* user_data) {
  FSDK_BStr_Clear(&gsignature_callback_data.sub_filter_);
  FSDK_BStr_Clear(&gsignature_callback_data.cert_file_path_);
  FSDK_BStr_Clear(&gsignature_callback_data.cert_file_password_);
  FSDK_BStr_Clear(&gsignature_callback_data.digest_context_->digest_);
  FSDK_BStr_Clear(&gsignature_callback_data.digest_context_->signed_data);

  free(gsignature_callback_data.digest_context_);
  free(user_data);
}

FS_BOOL gIsNeedPadData(void* user_data) { return FALSE; }
FSCertValidity gCheckCertificateValidity(void* user_data, const wchar_t* cert_path, const wchar_t* cert_password, void* client_data) {
  // User can check the validity of input certificate here.
  // If no need to check, just return e_CertValid.
  return e_FSCertValid;
}
java
import com.foxit.sdk.pdf.*;
...

// Implementation of SignatureCallback
class SignatureCallbackImpl extends SignatureCallback {
	private String sub_filter_;
	private DigestContext digest_context_ = null;
	byte[] arrall = null;

	SignatureCallbackImpl(String subfilter) {
		sub_filter_ = subfilter;
	}

	@Override
	public boolean startCalcDigest(FileReaderCallback var1, int[] var2,
			com.foxit.sdk.pdf.Signature var3, Object var4) {
		digest_context_ = new DigestContext(var1, var2, var2.length);
		return true;
	}

	@Override
	public int continueCalcDigest(Object var1, PauseCallback var2) {
		return com.foxit.sdk.common.Progressive.e_Finished;
	}

	@Override
	public byte[] getDigest(Object var1) {
		return arrall;
	}
	
	@Override
	public byte[] sign(byte[] digest, String cert_path, byte[] cert_password, int digest_algorithm, java.lang.Object client_data){
		String encryptStr = null;

		try {
			try {
				FileReaderCallback filehandler = digest_context_.file_read_callback_;
				{
					long size = filehandler.getSize();
					byte[] arr1 = new byte[digest_context_.byte_range_array_[1]];
					filehandler.readBlock(arr1,
							digest_context_.byte_range_array_[0],
							digest_context_.byte_range_array_[1]);
					byte[] arr2 = new byte[digest_context_.byte_range_array_[3]];
					filehandler.readBlock(arr2,
							digest_context_.byte_range_array_[2],
							digest_context_.byte_range_array_[3]);
					size = 0;
					arrall = new byte[(int) digest_context_.byte_range_array_[1]
							+ (int) digest_context_.byte_range_array_[3]];
					System.arraycopy(arr1, 0, arrall, 0, arr1.length);
					System.arraycopy(arr2, 0, arrall, arr1.length, arr2.length);
				}

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			encryptStr = CertUtil.SignMsg(arrall, signature.input_path
					+ "foxit_all.pfx", "123456");
			return encryptStr.getBytes();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

              @Override
               public byte[] sign(byte[] digest, StreamCallback cert_path, byte[] cert_password, int digest_algorithm, java.lang.Object client_data){
                             return null;
               }

	@Override
	public int verifySigState(byte[] var1, byte[] var2, Object var3) {
		byte[] arrall_verify = null;
		boolean verify_state = false;
		try {
			verify_state = CertUtil.VerifyMsg(new String(var2).toLowerCase(), arrall,
					signature.input_path + "foxit.cer");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return verify_state ? com.foxit.sdk.pdf.Signature.e_StateVerifyNoChange : com.foxit.sdk.pdf.Signature.e_StateVerifyChange;
	}
	
	@Override
	public boolean isNeedPadData() {return false;}
}
...
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"
#include "openssl/rsa.h"
#include "openssl/evp.h"
#include "openssl/objects.h"
#include "openssl/x509.h"
#include "openssl/err.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/pkcs12.h"
#include "openssl/rand.h"
#include "openssl/pkcs7.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

#include <string>

// some base type declarations
typedef std::string String;

// Used for implementing SignatureCallback.
class DigestContext {
public:
    DigestContext(id<FSFileReaderCallback> file_read_callback, NSArray<NSNumber *> *byte_range_array)
    : file_read_callback_(file_read_callback), byte_range_array_(byte_range_array) {}
    ~DigestContext() {}
    
    id<FSFileReaderCallback> GetFileReadCallback() {
        return file_read_callback_;
    }
    NSUInteger GetByteRangeSize() {
        return byte_range_array_.count;
    }
    unsigned int GetByteRangeElement(NSUInteger index) {
        if (!byte_range_array_)
            return 0;
        return [byte_range_array_[index] unsignedIntValue];
    }
    
    SHA_CTX sha_ctx_;
    
protected:
    id<FSFileReaderCallback> file_read_callback_;
    NSArray<NSNumber *> *byte_range_array_;
};

// Implementation of pdf::SignatureCallback
class SignatureCallbackImpl {
public:
    SignatureCallbackImpl(std::string subfilter)
    : sub_filter_(subfilter), digest_context_(NULL) {}
    ~SignatureCallbackImpl();
    
    void Release() {
        delete this;
    }
    bool StartCalcDigest(id<FSFileReaderCallback> file, NSArray<NSNumber *> *byte_range_array, FSSignature *signature, const void *client_data);
    FSProgressiveState ContinueCalcDigest(const void *client_data, id<FSPauseCallback> pause);
    NSData *GetDigest(const void *client_data);
    NSData *Sign(const void *digest, uint32 digest_length, std::string cert_path,
                 std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                 void *client_data);
    NSData *Sign(const void *digest, uint32 digest_length, id<FSFileStreamCallback> cert_file_stream,
                 std::string password, FSSignatureDigestAlgorithm digest_algorithm, void *client_data);

    FSSignatureStates VerifySigState(const void *digest, uint32 digest_length,
                                     const void *signed_data, uint32 signed_data_len,
                                     void *client_data);
    bool IsNeedPadData() { return false; }
    
protected:
    bool GetTextFromFile(unsigned char *plainString);
    
    unsigned char *PKCS7Sign(std::string cert_file_path, String cert_file_password,
                             String plain_text, int &signed_data_size);
    bool PKCS7VerifySignature(String signed_data, String plain_text);
    bool ParseP12File(std::string cert_file_path, String cert_file_password,
                      EVP_PKEY **pkey, X509 **x509, STACK_OF(X509) * *ca);
    ASN1_INTEGER *CreateNonce(int bits);
    
private:
    std::string sub_filter_;
    DigestContext *digest_context_;
    
    std::string cert_file_path_;
    std::string cert_file_password_;
};

#define FREE_CERT_KEY if(pkey)\
EVP_PKEY_free(pkey);\
if(x509)\
X509_free(x509);\
if(ca)\
sk_X509_free(ca);

void InitializeOpenssl() {
    //      SSLeay_add_all_algorithms();
}

SignatureCallbackImpl::~SignatureCallbackImpl() {
    if (digest_context_) {
        delete digest_context_;
        digest_context_ = NULL;
    }
}

bool SignatureCallbackImpl::GetTextFromFile(unsigned char *file_buffer) {
    if (!digest_context_ || !digest_context_->GetFileReadCallback())
        return false;
    id<FSFileReaderCallback> file_read = digest_context_->GetFileReadCallback();
    NSData *data = [file_read readBlock:digest_context_->GetByteRangeElement(0) size:digest_context_->GetByteRangeElement(1)];
    [data getBytes:file_buffer length:data.length];
    
    data = [file_read readBlock:digest_context_->GetByteRangeElement(2) size:digest_context_->GetByteRangeElement(3)];
    [data getBytes:file_buffer + (digest_context_->GetByteRangeElement(1) - digest_context_->GetByteRangeElement(0)) length:data.length];
    return true;
}

bool SignatureCallbackImpl::StartCalcDigest(id<FSFileReaderCallback> file, NSArray<NSNumber *> *byte_range_array, FSSignature *signature, const void *client_data) {
    if (digest_context_) {
        delete digest_context_;
        digest_context_ = NULL;
    }
    digest_context_ = new DigestContext(file, byte_range_array);
    if (!SHA1_Init(&digest_context_->sha_ctx_)) {
        delete digest_context_;
        digest_context_ = NULL;
        return false;
    }
    return true;
}

FSProgressiveState SignatureCallbackImpl::ContinueCalcDigest(const void *client_data, id<FSPauseCallback> pause) {
    if (!digest_context_)
        return FSProgressiveError;
    
    uint32 file_length = digest_context_->GetByteRangeElement(1) + digest_context_->GetByteRangeElement(3);
    unsigned char *file_buffer = (unsigned char *) malloc(file_length);
    if (!file_buffer || !GetTextFromFile(file_buffer))
        return FSProgressiveError;
    
    SHA1_Update(&digest_context_->sha_ctx_, file_buffer, file_length);
    free(file_buffer);
    return FSProgressiveFinished;
}

NSData *SignatureCallbackImpl::GetDigest(const void *client_data) {
    if (!digest_context_)
        return nil;
    unsigned char *md = reinterpret_cast<unsigned char *>(OPENSSL_malloc((SHA_DIGEST_LENGTH) * sizeof(unsigned char)));
    if (1 != SHA1_Final(md, &digest_context_->sha_ctx_))
        return nil;
    NSData *digest = [NSData dataWithBytes:reinterpret_cast<const void *>(md) length:SHA_DIGEST_LENGTH];
    OPENSSL_free(md);
    return digest;
}

NSData *SignatureCallbackImpl::Sign(const void *digest, uint32 digest_length, std::string cert_path,
                                    std::string password, FSSignatureDigestAlgorithm digest_algorithm,
                                    void *client_data) {
    if (!digest_context_)
        return nil;
    String plain_text;
    if ("adbe.pkcs7.sha1" == sub_filter_) {
        plain_text = String((const char *) digest, digest_length);
    }
    int signed_data_length = 0;
    unsigned char *signed_data_buffer = PKCS7Sign(cert_path, password,
                                                  plain_text, signed_data_length);
    if (!signed_data_buffer)
        return nil;
    
    NSData *signed_data = [NSData dataWithBytes:(const void *) signed_data_buffer length:signed_data_length];
    free(signed_data_buffer);
    return signed_data;
}

NSData *Sign(const void *digest, uint32 digest_length, id<FSFileStreamCallback> cert_file_stream,
                 std::string password, FSSignatureDigestAlgorithm digest_algorithm, void *client_data) {
    return nil;
}

FSSignatureStates SignatureCallbackImpl::VerifySigState(const void *digest, uint32 digest_length,
                                                        const void *signed_data, uint32 signed_data_len, void *client_data) {
    // Usually, the content of a signature field is containing 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 (!digest_context_)
        return FSSignatureStateVerifyErrorData;
    String plain_text;
    unsigned char *file_buffer = NULL;
    if ("adbe.pkcs7.sha1" == sub_filter_) {
        plain_text = String(reinterpret_cast<const char *>(digest), digest_length);
    } else {
        return FSSignatureStateUnknown;
    }
    
    String signed_data_str = String(reinterpret_cast<const char *>(signed_data), signed_data_len);
    bool ret = PKCS7VerifySignature(signed_data_str, plain_text);
    if (file_buffer)
        free(file_buffer);
return ret ? FSSignatureStateVerifyNoChange: FSSignatureStateVerifyChange;
}

ASN1_INTEGER *SignatureCallbackImpl::CreateNonce(int bits) {
    unsigned char buf[20];
    int len = (bits - 1) / 8 + 1;
    // Generating random byte sequence.
    if (len > (int) sizeof(buf)) {
        return NULL;
    }
    if (RAND_bytes(buf, len) <= 0) {
        return NULL;
    }
    // Find the first non-zero byte and creating ASN1_INTEGER object.
    int i = 0;
    for (i = 0; i < len && !buf[i]; ++i)
        ;
    ASN1_INTEGER *nonce = NULL;
    if (!(nonce = ASN1_INTEGER_new())) {
        ASN1_INTEGER_free(nonce);
        return NULL;
    }
    OPENSSL_free(nonce->data);
    // Allocate at least one byte.
    nonce->length = len - i;
    if (!(nonce->data = reinterpret_cast<unsigned char *>(OPENSSL_malloc(nonce->length + 1)))) {
        ASN1_INTEGER_free(nonce);
        return NULL;
    }
    memcpy(nonce->data, buf + i, nonce->length);
    return nonce;
}

bool SignatureCallbackImpl::ParseP12File(std::string cert_file_path, String cert_file_password,
                                         EVP_PKEY **pkey, X509 **x509, STACK_OF(X509) * *ca) {
    FILE *file = NULL;
#if defined(_WIN32) || defined(_WIN64)
    _wfopen_s(&file, cert_file_path, @"rb");
#else
    file = fopen(cert_file_path.c_str(), "rb");
#endif  // defined(_WIN32) || defined(_WIN64)
    if (!file) {
        return false;
    }
    
    PKCS12 *pkcs12 = d2i_PKCS12_fp(file, NULL);
    fclose(file);
    if (!pkcs12) {
        return false;
    }
    
    if (!PKCS12_parse(pkcs12, cert_file_password.c_str(), pkey, x509, ca)) {
        return false;
    }
    
    PKCS12_free(pkcs12);
    if (!pkey)
        return false;
    return true;
}

unsigned char *SignatureCallbackImpl::PKCS7Sign(std::string cert_file_path, String cert_file_password,
                                                String plain_text, int &signed_data_size) {
    PKCS7 *p7 = NULL;
    EVP_PKEY *pkey = NULL;
    X509 *x509 = NULL;
    STACK_OF(X509) *ca = NULL;
    if (!ParseP12File(cert_file_path, cert_file_password, &pkey, &x509, &ca))
        return NULL;
    
    p7 = PKCS7_new();
    PKCS7_set_type(p7, NID_pkcs7_signed);
    PKCS7_content_new(p7, NID_pkcs7_data);
    
    // Application should not judge the sign algorithm with the content's length.
    // Here, just for convenient;
    if (plain_text.size() > 32)
        PKCS7_ctrl(p7, PKCS7_OP_SET_DETACHED_SIGNATURE, 1, NULL);
    
    PKCS7_SIGNER_INFO *signer_info = PKCS7_add_signature(p7, x509, pkey, EVP_sha1());
    signer_info = NULL;
    PKCS7_add_certificate(p7, x509);
    
# define CHECKED_STACK_OF(type, p) \
((_STACK*) (1 ? p : (STACK_OF(type)*)0))
    for (int i = 0; i < sk_num(CHECKED_STACK_OF(X509, ca)); i++)
        PKCS7_add_certificate(p7, (X509 *) sk_value(CHECKED_STACK_OF(X509, ca), i));
    
    // Set source data to BIO.
    BIO *p7bio = PKCS7_dataInit(p7, NULL);
    BIO_write(p7bio, plain_text.c_str(), (int) plain_text.size());
    PKCS7_dataFinal(p7, p7bio);
    
    FREE_CERT_KEY;
    BIO_free_all(p7bio);
    // Get signed data.
    unsigned long der_length = i2d_PKCS7(p7, NULL);
    unsigned char *der = reinterpret_cast<unsigned char *>(malloc(der_length));
    memset(der, 0, der_length);
    unsigned char *der_temp = der;
    i2d_PKCS7(p7, &der_temp);
    PKCS7_free(p7);
    signed_data_size = (int) der_length;
    return (unsigned char *) der;
}

bool SignatureCallbackImpl::PKCS7VerifySignature(String signed_data, String plain_text) {
    // Retain PKCS7 object from signed data.
    BIO *vin = BIO_new_mem_buf((void *) signed_data.c_str(), (int) signed_data.size());
    PKCS7 *p7 = d2i_PKCS7_bio(vin, NULL);
    STACK_OF(PKCS7_SIGNER_INFO) *sk = PKCS7_get_signer_info(p7);
    int sign_count = sk_PKCS7_SIGNER_INFO_num(sk);
    
    //      int length = 0;
    bool bSigAppr = false;
    //      unsigned char *p = NULL;
    for (int i = 0; i < sign_count; i++) {
        PKCS7_SIGNER_INFO *sign_info = sk_PKCS7_SIGNER_INFO_value(sk, i);
        
        BIO *p7bio = BIO_new_mem_buf((void *) plain_text.c_str(), (int) plain_text.size());
        X509 *x509 = PKCS7_cert_from_signer_info(p7, sign_info);
        x509 = NULL;
        if (1 == PKCS7_verify(p7, NULL, NULL, p7bio, NULL, PKCS7_NOVERIFY))
            bSigAppr = true;
        BIO_free(p7bio);
    }
    PKCS7_free(p7);
    BIO_free(vin);
    return bSigAppr;
}
...
js
const FSDK = require("@foxitsoftware/foxit-pdf-sdk-node");

...
let doc = new FSDK.PDFDoc("Sample.pdf");
csharp
using System;
using System.Runtime.InteropServices;
using foxit;
using foxit.common;
using foxit.common.fxcrt;
using foxit.pdf;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography;
using System.Text;

namespace signatureCS
{
    class SignAndVerifyMsg
    {
        public static X509Certificate2 GetCertificate(string commonName, StoreName storeName)
        {
            X509Certificate2 certificate = null;

            // Look for a certificate in the local machine store.
            var store = new X509Store(storeName, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            foreach (var cert in store.Certificates)
            {
                var subjectNames = cert.SubjectName.Name.Split(',');
                foreach (var subjectName in subjectNames)
                {
                    string cn = " CN=" + commonName;
                    if (subjectName.Equals(cn))
                    {
                        certificate = cert;
                        break;
                    }
                }

                if (certificate != null)
                {
                    break;
                }
            }
            return certificate;
        }
        public static bool SignMsg(byte[] to_be_signed_data, string commonName, StoreName storeName, ref byte[] signed_data)
        {
            X509Certificate2 cert = GetCertificate(commonName, storeName);
            if (cert == null)
            {
                cert = GetCertificate(commonName, StoreName.Root);
            }
            CmsSigner signer = new CmsSigner(cert);
            signer.DigestAlgorithm = new Oid("1.3.14.3.2.26", "sha1");

            signer.IncludeOption = X509IncludeOption.WholeChain;

            ContentInfo signedData = new ContentInfo(to_be_signed_data);
            SignedCms cms = new SignedCms(signedData, false);
            cms.ComputeSignature(signer);
            signed_data = cms.Encode();

            return true;
        }

        public static bool VerifyMsg(byte[] verify_data, byte[] signed_data)
        {
            Boolean b = true;
            try
            {
                ContentInfo signedData = new ContentInfo(verify_data);
                SignedCms cms = new SignedCms(signedData, false);
                cms.Decode(signed_data);
                // Check Signature
                cms.CheckSignature(true);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                b = false;
            }
            return b;
        }
}

// Used for implementing SignatureCallback.
    class DigestContext
    {
        public FileReaderCallback file_read_callback_;
        public System.IntPtr byte_range_array_;
        public int byte_range_array_size_;

        public DigestContext(FileReaderCallback file_read_callback, System.IntPtr byte_range_array, int byte_range_array_size)
        {
            this.file_read_callback_ = file_read_callback;
            this.byte_range_array_ = byte_range_array;
            this.byte_range_array_size_ = byte_range_array_size;
        }
    };

    // Implementation of pdf::SignatureCallback
    class SignatureCallbackImpl : SignatureCallback
    {
        private DigestContext digest_context_ = null;
        byte[] arrall = null;

        public SignatureCallbackImpl()
        {
        }

        public override bool StartCalcDigest(FileReaderCallback file, System.IntPtr byte_range_array, int size_of_array, Signature signature, IntPtr client_data)
        {
            digest_context_ = new DigestContext(file, byte_range_array, size_of_array);
            return true;
        }

        public override Progressive.State ContinueCalcDigest(IntPtr client_data, PauseCallback pause)
        {
            FileReaderCallback filehandler = digest_context_.file_read_callback_;
            int[] int_array = new int[digest_context_.byte_range_array_size_];
            Marshal.Copy(digest_context_.byte_range_array_, int_array, 0, digest_context_.byte_range_array_size_);

            byte[] arr1 = new byte[int_array[1]];
            IntPtr buffer1 = Marshal.AllocHGlobal(int_array[1]);

            filehandler.ReadBlock(buffer1,
                    int_array[0],
                    (uint)int_array[1]);

            Marshal.Copy(buffer1, arr1, 0, int_array[1]);

            byte[] arr2 = new byte[int_array[3]];
            IntPtr buffer2 = Marshal.AllocHGlobal(int_array[3]);
            filehandler.ReadBlock(buffer2,
                    int_array[2],
                    (uint)int_array[3]);
            Marshal.Copy(buffer2, arr2, 0, int_array[3]);

            byte[] arrall_temp = new byte[(int)int_array[1]
                    + (int)int_array[3]];
            Array.Copy(arr1, 0, arrall_temp, 0, arr1.Length);
            Array.Copy(arr2, 0, arrall_temp, arr1.Length, arr2.Length);

            Marshal.FreeHGlobal(buffer1);
            Marshal.FreeHGlobal(buffer2);

            HashAlgorithm hash = HashAlgorithm.Create("sha1");
            hash.Initialize();
            arrall = hash.ComputeHash(arrall_temp);

            return Progressive.State.e_Finished;
        }

        public override byte[] GetDigest(IntPtr client_data)
        {
            if (arrall != null)
            {
                return arrall;
            }
            return null;
        }

        public override byte[] Sign(IntPtr digest, int digest_length, string cert_path, byte[] password, Signature.DigestAlgorithm digest_algorithm, IntPtr client_data)
        {
            byte[] byte_digest = new byte[digest_length];
            Marshal.Copy(digest, byte_digest, 0, digest_length);

            byte[] encryptStr = null;
            SignAndVerifyMsg.SignMsg(byte_digest, "test", StoreName.My, ref encryptStr);
            return encryptStr;
        }

        public override byte[] Sign(IntPtr digest, int digest_length, StreamCallback cert_path, byte[] password, Signature.DigestAlgorithm digest_algorithm, IntPtr client_data)
        {
            return null;
        }

        public override uint VerifySigState(global::System.IntPtr digest, int digest_length, global::System.IntPtr signed_data, int signed_data_len, global::System.IntPtr client_data)
        {
            byte[] byte_signdata = new byte[signed_data_len];
            Marshal.Copy(signed_data, byte_signdata, 0, signed_data_len);

            byte[] byte_digest = new byte[digest_length];
            Marshal.Copy(digest, byte_digest, 0, digest_length);

            bool verified = SignAndVerifyMsg.VerifyMsg(byte_digest, byte_signdata);

            return verified ? Convert.ToUInt32(Signature.States.e_StateVerifyNoChange) : Convert.ToUInt32(Signature.States.e_StateVerifyChange);
        }

        public override void Dispose()
        {
        }

        public override void Release()
        {
        }

        public override bool IsNeedPadData() { return false; }

        public override SignatureCallback.CertValidity CheckCertificateValidity(string cert_path, byte[] cert_password, global::System.IntPtr client_data)
        {
            // User can check the validity of input certificate here.
            // If no need to check, just return e_CertValid.
            return SignatureCallback.CertValidity.e_CertValid;
        }
}

    class signature
    {
        private const string input_path = "../input_files/";
        private const string output_path = "../output_files/pdf2office/";

        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 Signature AddSiganture(PDFPage pdf_page, string sub_filter)
        {
            float page_height = pdf_page.GetHeight();
            float page_width = pdf_page.GetWidth();
            RectF new_sig_rect = new RectF(0, (float)(page_height * 0.9), (float)(page_width * 0.4), page_height);
            // Add a new signature to page.
            Signature new_sig = pdf_page.AddSignature(new_sig_rect);
            if (new_sig.IsEmpty()) return null;
            // Set values for the new signature.
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameSigner, "Foxit PDF SDK");
            String new_value = String.Format("As a sample for subfilter \"{0}\"", sub_filter);
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameReason, new_value);
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameContactInfo, "support@foxitsoftware.com");
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameDN, "CN=CN,MAIL=MAIL@MAIL.COM");
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameLocation, "Fuzhou, China");
            new_value = String.Format("As a sample for subfilter \"{0}\"", sub_filter);
            new_sig.SetKeyValue(Signature.KeyName.e_KeyNameText, new_value);
            foxit.common.DateTime sign_time = GetLocalDateTime();
            new_sig.SetSignTime(sign_time);
            String image_file_path = input_path + "FoxitLogo.jpg";
            using (Image image = new Image(image_file_path))
            {
                new_sig.SetImage(image, 0);
                // 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 | Signature.APFlags.e_APFlagBitmap);
                new_sig.SetAppearanceFlags(ap_flags);
            }

            return new_sig;
        }
        static SignatureCallbackImpl sig_callback = new SignatureCallbackImpl();
        static void Main(string[] args)
        {
            System.IO.Directory.CreateDirectory(output_path);
            string sn = " ";
            string key = " ";
            // Initialize library
            ErrorCode error_code = Library.Initialize(sn, key);
            if (error_code != ErrorCode.e_ErrSuccess)
            {
                if (ErrorCode.e_ErrInvalidLicense == error_code)
                    Console.WriteLine("[Failed] Current used Foxit PDF Conversion SDK key information is invalid.");
                else
                    Console.WriteLine("Library Initialize Error: {0}", error_code);
                return;
            }

            try
            {
                using (PDFDoc pdf_doc = new PDFDoc(input_path + "AboutFoxit.pdf"))
                {
                    pdf_doc.StartLoad(null, true, null);
                    using (PDFPage pdf_page = pdf_doc.GetPage(0))
                    {
                        string filter = "Adobe.PPKLite";

                        string sub_filter = "adbe.pkcs7.sha1";
                        Library.RegisterSignatureCallback(filter, sub_filter, sig_callback);
                        // Add a new signature to first page.
                        using (Signature new_signature = AddSiganture(pdf_page, sub_filter))
                        {
                            new_signature.SetFilter(filter);
                            new_signature.SetSubFilter(sub_filter);
                            String signed_pdf_path = output_path + "signed_newsignature.pdf";
                            new_signature.StartSign("", Encoding.ASCII.GetBytes(""),
                                  Signature.DigestAlgorithm.e_DigestSHA1, signed_pdf_path, IntPtr.Zero, null);
                        }
                    }
                }
            }
            catch (PDFException e)
            {
                switch (e.GetErrorCode())
                {
                    case ErrorCode.e_ErrNoPDF2OfficeModuleRight:
                        Console.WriteLine("[Failed] Conversion module is not contained in current Foxit PDF Conversion SDK keys.");
                        break;
                    default:
                        Console.WriteLine(e.Message);
                        break;
                }
            }
            Library.Release();
        }
    }
}