Foxit PDF SDK for Windows

How to Apply Signatures in Foxit PDF SDK (C++)

The PDF Signature module can be used to create and sign with digital signatures for PDF documents. This protects the security of documents and prevents it from being tampered with maliciously. It can let the receiver ensure that the document is released by the signer and the contents of the document are complete and unchanged. Foxit PDF SDK provides APIs to create a digital signature, verify the validity of a signature, delete an existing digital signature, get and set properties of a digital signature, display a signature and customize the appearance of the signature form fields.

Note: Foxit PDF SDK provides default Signature callbacks which supports the following two types of signature filter and subfilter:

(1) filter: Adobe.PPKLite subfilter: adbe.pkcs7.detached
(2) filter: Adobe.PPKLite subfilter: adbe.pkcs7.sha1

If you use one of the above signature filters and subfilters, you can sign a PDF document and verify the validity of signature by default without needing to register a custom callback.

Example:

How to sign the PDF document with a signature

#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());

How to implement signature callback function of signing

#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(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(OPENSSL_malloc((SHA_DIGEST_LENGTH)*sizeof(unsigned char)));
 if (1 != SHA1_Final(md, &digest_context_->sha_ctx_))
 return "";
 String digest = String(reinterpret_cast(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 certs.
 // 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(digest), digest_length);
 } else {
 return Signature::e_StateUnknown;
 }
 String signed_data_str = String(reinterpret_cast(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(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(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

Updated on April 18, 2019

Was this article helpful?
Thanks for your feedback. If you have a comment on how to improve the article, you can write it here: