Skip to content

Instantly share code, notes, and snippets.

@diorahman
Created October 15, 2014 08:50
Show Gist options
  • Save diorahman/c423252e9a24f0c93ee0 to your computer and use it in GitHub Desktop.
Save diorahman/c423252e9a24f0c93ee0 to your computer and use it in GitHub Desktop.
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2014 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of high-level digital signature API in PDFNet.
//
// The following steps are typically used to add a digital signature to a PDF:
//
// 1. Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or validate/check a
// digital signature.
// 2. Create an instance of the implemented SignatureHandler and register it with PDFDoc with
// pdfdoc.AddSignatureHandler(). The method returns an ID that can be used later to associate a SignatureHandler
// with a field.
// 3. Find the required 'e_signature' field in the existing document or create a new field.
// 4. Call field.UseSignatureHandler() with the ID of your handler.
// 5. Call pdfdoc.Save()
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------
// To build and run this sample with OpenSSL, please specify OpenSSL include & lib paths to project settings.
//
// In MSVC, this can be done by opening the DigitalSignatureTest project's properties. Go to Configuration Properties ->
// C/C++ -> General -> Additional Include Directories. Add the path to the OpenSSL headers here. Next, go to
// Configuration Properties -> Linker -> General -> Additional Library Directories. Add the path to the OpenSSL libraries
// here. Finally, under Configuration Properties -> Linker -> Input -> Additional Dependencies, add libeay32.lib,
// crypt32.lib, and advapi32.lib in the list.
//
// For GCC, modify the Makefile, add -lcrypto to the $(LIBS) variable. If OpenSSL is installed elsewhere, it may be
// necessary to add the path to the headers in the $(INCLUDE) variable as well as the location of either libcrypto.a or
// libcrypto.so/libcrypto.dylib.
//
#define USE_STD_SIGNATURE_HANDLER 1 // Comment this line if using the OpenSSLSignatureHandler.
// standard library includes
#include <cstdio>
#include <iostream>
#include <vector>
// PDFNetC includes
#include <Common/Exception.h>
#include <Common/UString.h>
#include <PDF/Page.h>
#include <PDF/Annot.h>
#include <PDF/Annots/Widget.h>
#include <PDF/Date.h>
#include <PDF/Element.h>
#include <PDF/ElementBuilder.h>
#include <PDF/ElementWriter.h>
#include <PDF/Field.h>
#include <PDF/Image.h>
#include <PDF/PDFDoc.h>
#include <PDF/PDFNet.h>
#include <SDF/SignatureHandler.h>
#if (!USE_STD_SIGNATURE_HANDLER)
// OpenSSL includes
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif // (!USE_STD_SIGNATURE_HANDLER)
using namespace std;
using namespace pdftron;
using namespace pdftron::SDF;
using namespace pdftron::PDF::Annots;
using namespace pdftron::PDF;
#if (!USE_STD_SIGNATURE_HANDLER)
//
// Extend SignatureHandler by using OpenSSL signing utilities.
//
class OpenSSLSignatureHandler : public SignatureHandler
{
public:
OpenSSLSignatureHandler(const char* in_pfxfile, const char* in_password) : m_pfxfile(in_pfxfile), m_password(in_password)
{
FILE* fp = fopen(in_pfxfile, "rb");
if (fp == NULL)
throw (Common::Exception("Cannot open private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot open private key."));
PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (p12 == NULL)
throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));
mp_pkey = NULL;
mp_x509 = NULL;
mp_ca = NULL;
int parseResult = PKCS12_parse(p12, in_password, &mp_pkey, &mp_x509, &mp_ca);
PKCS12_free(p12);
if (parseResult == 0)
throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));
Reset();
}
virtual UString GetName() const
{
return (UString("Adobe.PPKLite"));
}
virtual void AppendData(const std::vector<pdftron::UInt8>& in_data)
{
SHA1_Update(&m_sha_ctx, (const void*) &(in_data[0]), in_data.size());
return;
}
virtual bool Reset()
{
m_digest.resize(0);
m_digest.clear();
SHA1_Init(&m_sha_ctx);
return (true);
}
virtual std::vector<pdftron::UInt8> CreateSignature()
{
if (m_digest.size() == 0) {
m_digest.resize(SHA_DIGEST_LENGTH);
SHA1_Final(&(m_digest[0]), &m_sha_ctx);
}
PKCS7* p7 = PKCS7_new();
PKCS7_set_type(p7, NID_pkcs7_signed);
PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha1());
PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
PKCS7_add0_attrib_signing_time(p7Si, NULL);
PKCS7_add1_attrib_digest(p7Si, &(m_digest[0]), m_digest.size());
PKCS7_add_certificate(p7, mp_x509);
for (int c = 0; c < sk_X509_num(mp_ca); c++) {
X509* cert = sk_X509_value(mp_ca, c);
PKCS7_add_certificate(p7, cert);
}
PKCS7_set_detached(p7, 1);
PKCS7_content_new(p7, NID_pkcs7_data);
PKCS7_SIGNER_INFO_sign(p7Si);
int p7Len = i2d_PKCS7(p7, NULL);
std::vector<unsigned char> result(p7Len);
UInt8* pP7Buf = &(result[0]);
i2d_PKCS7(p7, &pP7Buf);
PKCS7_free(p7);
return (result);
}
virtual OpenSSLSignatureHandler* Clone() const
{
return (new OpenSSLSignatureHandler(m_pfxfile.c_str(), m_password.c_str()));
}
virtual ~OpenSSLSignatureHandler()
{
sk_X509_free(mp_ca);
X509_free(mp_x509);
EVP_PKEY_free(mp_pkey);
}
private:
std::vector<UInt8> m_digest;
std::string m_pfxfile;
std::string m_password;
SHA_CTX m_sha_ctx;
EVP_PKEY* mp_pkey; // private key
X509* mp_x509; // signing certificate
STACK_OF(X509)* mp_ca; // certificate chain up to the CA
}; // class OpenSSLSignatureHandler
#endif // (!USE_STD_SIGNATURE_HANDLER)
//
// This functions add an approval signature to the PDF document. The original PDF document contains a blank form field
// that is prepared for a user to sign. The following code demonstrate how to sign this document using PDFNet.
//
bool SignPDF(void)
{
UString infile("../../TestFiles/doc_to_sign.pdf");
UString outfile("../../TestFiles/Output/signed_doc.pdf");
UString certfile("../../TestFiles/pdftron.pfx");
UString imagefile("../../TestFiles/signature.jpg");
bool result = true;
try {
cout << "Signing PDF document: \"" << infile << "\".\n";
// Open an existing PDF
PDFDoc doc(infile);
// Add an StdSignatureHandler instance to PDFDoc, making sure to keep track of it using the ID returned.
SDF::SignatureHandlerId sigHandlerId = doc.AddStdSignatureHandler(certfile, UString("password"));
// When using OpenSSLSignatureHandler class, uncomment the following lines and comment the line above.
// Create a new instance of the SignatureHandler.
//OpenSSLSignatureHandler sigHandler(certfile.ConvertToUtf8().c_str(), "password");
// Add the SignatureHandler instance to PDFDoc, making sure to keep track of it using the ID returned.
//SDF::SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
// Obtain the signature form field from the PDFDoc via Annotation.
Field sigField = doc.GetField(UString("Signature1"));
Widget widgetAnnot(sigField.GetSDFObj());
// Tell PDFNetC to use the SignatureHandler created to sign the new signature form field.
SDF::Obj sigDict = sigField.UseSignatureHandler(sigHandlerId);
// Add more information to the signature dictionary.
//sigDict.PutName("SubFilter", "adbe.pkcs7.detached");
sigDict.PutString("Name", "PDFTron");
sigDict.PutString("Location", "Vancouver, BC");
sigDict.PutString("Reason", "Document verification.");
// Add the signature appearance.
ElementWriter apWriter;
ElementBuilder apBuilder;
apWriter.Begin(doc);
Image sigImg = Image::Create(doc, imagefile);
double w = sigImg.GetImageWidth(), h = sigImg.GetImageHeight();
Element apElement = apBuilder.CreateImage(sigImg, 0, 0, w, h);
apWriter.WritePlacedElement(apElement);
SDF::Obj apObj = apWriter.End();
apObj.PutRect("BBox", 0, 0, w, h);
apObj.PutName("Subtype", "Form");
apObj.PutName("Type", "XObject");
apWriter.Begin(doc);
apElement = apBuilder.CreateForm(apObj);
apWriter.WritePlacedElement(apElement);
apObj = apWriter.End();
apObj.PutRect("BBox", 0, 0, w, h);
apObj.PutName("Subtype", "Form");
apObj.PutName("Type", "XObject");
widgetAnnot.SetAppearance(apObj);
widgetAnnot.RefreshAppearance();
// Save the PDFDoc. Once the method below is called, PDFNetC will also sign the document using the information
// provided.
doc.Save(outfile, 0, NULL);
cout << "Finished signing PDF document.\n";
}
catch (Common::Exception& e) {
cerr << e << "\n";
result = false;
}
catch (exception& e) {
cerr << e.what() << "\n";
result = false;
}
catch (...) {
cerr << "Unknown exception.\n";
result = false;
}
return (result);
}
//
// Adds a certification signature to the PDF document. Certifying a document is like notarizing a document. Unlike
// approval signatures, there can be only one certification per PDF document. Only the first signature in the PDF
// document can be used as the certification signature. The process of certifying a document is almost exactly the same
// as adding approval signatures with the exception of certification signatures requires an entry in the "Perms"
// dictionary.
//
bool CertifyPDF(void)
{
UString infile("../../TestFiles/newsletter.pdf");
UString outfile("../../TestFiles/Output/newsletter_certified.pdf");
UString certfile("../../TestFiles/pdftron.pfx");
bool result = true;
try {
cout << "Certifying PDF document: \"" << infile << "\".\n";
// Open an existing PDF
PDFDoc doc(infile);
// Add an StdSignatureHandler instance to PDFDoc, making sure to keep track of it using the ID returned.
SDF::SignatureHandlerId sigHandlerId = doc.AddStdSignatureHandler(certfile, UString("password"));
// When using OpenSSLSignatureHandler class, uncomment the following lines and comment the line above.
// Create a new instance of the SignatureHandler.
//OpenSSLSignatureHandler sigHandler(certfile.ConvertToUtf8().c_str(), "password");
// Add the SignatureHandler instance to PDFDoc, making sure to keep track of it using the ID returned.
//SDF::SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
// Create new signature form field in the PDFDoc.
Field sigField = doc.FieldCreate(UString("Signature1"), Field::e_signature);
Page page1 = doc.GetPage(1);
Widget widgetAnnot = pdftron::PDF::Annots::Widget::Create(doc.GetSDFDoc(), Rect(0, 0, 0, 0), sigField);
page1.AnnotPushBack(widgetAnnot);
widgetAnnot.SetPage(page1);
SDF::Obj widgetObj = widgetAnnot.GetSDFObj();
widgetObj.PutNumber("F", 132);
widgetObj.PutName("Type", "Annot");
// Tell PDFNetC to use the SignatureHandler created to sign the new signature form field.
SDF::Obj sigDict = sigField.UseSignatureHandler(sigHandlerId);
// Add more information to the signature dictionary.
sigDict.PutName("SubFilter", "adbe.pkcs7.detached");
sigDict.PutString("Name", "PDFTron");
sigDict.PutString("Location", "Vancouver, BC");
sigDict.PutString("Reason", "Document verification.");
// Appearance can be added to the widget annotation. Please see the "SignPDF()" function for details.
// Add this sigDict as DocMDP in Perms dictionary from root
SDF::Obj root = doc.GetRoot();
SDF::Obj perms = root.PutDict("Perms");
// add the sigDict as DocMDP (indirect) in Perms
perms.Put("DocMDP", sigDict);
// add the additional DocMDP transform params
SDF::Obj refObj = sigDict.PutArray("Reference");
SDF::Obj transform = refObj.PushBackDict();
transform.PutName("TransformMethod", "DocMDP");
transform.PutName("Type", "SigRef");
SDF::Obj transformParams = transform.PutDict("TransformParams");
transformParams.PutNumber("P", 1); // Set permissions as necessary.
transformParams.PutName("Type", "TransformParams");
transformParams.PutName("V", "1.2");
// Save the PDFDoc. Once the method below is called, PDFNetC will also sign the document using the information
// provided.
doc.Save(outfile, 0, NULL);
cout << "Finished certifying PDF document.\n";
}
catch (Common::Exception& e) {
cerr << e << "\n";
result = false;
}
catch (exception& e) {
cerr << e.what() << "\n";
result = false;
}
catch (...) {
cerr << "Unknown exception.\n";
result = false;
}
return (result);
}
int main(int argc, char** argv)
{
// Initialize PDFNetC
PDFNet::Initialize();
#if (!USE_STD_SIGNATURE_HANDLER)
// Initialize OpenSSL library
CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
#endif // (!USE_STD_SIGNATURE_HANDLER)
bool result = true;
if (!SignPDF())
result = false;
if (!CertifyPDF())
result = false;
// Clean up code
PDFNet::Terminate();
#if (!USE_STD_SIGNATURE_HANDLER)
ERR_free_strings();
EVP_cleanup();
#endif // (!USE_STD_SIGNATURE_HANDLER)
if (!result) {
cout << "\nTests failed.\n";
return 1;
}
cout << "\nAll tests passed.\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment