Some test text!

menu
search

Digitally sign PDF files in Obj-C

Sample Obj-C code to use PDFTron SDK's high-level digital signature API for digitally signing and/or certifying PDF files. Learn more about our PDF Digital Signature Library.

Get StartedSamplesDownload

To run this sample, get started with a free trial of PDFTron SDK.

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2019 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 reflect typical intended usage of the digital signature API:
//
//	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//	
//	1.	EITHER: 
//		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//		-OR-
//		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//	
//	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//		
//	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//		certain types of changes to be made to the document without invalidating the cryptographic digital signature's hash once it
//		is signed.]
//		
//	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//		a.	Taking a PKCS #12 keyfile path and its password
//		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
//		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//			in the following fashion: 
//			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//					validate/check a digital signature.
//			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//		NOTE: It is only possible to sign/certify one signature per call to the Save function.
//	
//	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic hash to it.
//		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//		so as to not invalidate the other signature's('s) cryptographic hashes. 
//
// 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, please specify OpenSSL include & lib paths to the Makefile
//
// 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.

// Note for iOS development: This code can be used to digitally sign PDFs in iOS devices. When using the code signing
// part of this code, it will be necessary to compile OpenSSL for iOS.

#define USE_STD_SIGNATURE_HANDLER 1 // Comment out this line if you intend to use OpenSSLSignatureHandler rather than StdSignatureHandler.

#import <CoreFoundation/CoreFoundation.h>

#import <OBJC/PDFNetOBJC.h>
//////////////////// Here follows an example of how to implement a custom signature handler. //////////
#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>

// Override SignatureHandler
@interface OpenSSLSignatureHandler : SignatureHandler
{
    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
}
- (NSString*) GetName;
- (void) AppendData: (NSData*)data;
- (BOOL) Reset;
- (NSData*) CreateSignature;
- (SignatureHandler*) Clone;
- (id) init: (NSString*) pfxfile password: (NSString*) password;
- (void) dealloc;
@end // interface OpenSSLSignatureHandler

@implementation OpenSSLSignatureHandler
- (NSString*) GetName
{
    return (@"Adobe.PPKLite");
}
- (void) AppendData: (NSData*)data
{
    SHA1_Update(&m_sha_ctx, [data bytes], [data length]);
    return;
}
- (BOOL) Reset
{
    SHA1_Init(&m_sha_ctx);
    return (YES);
}
- (NSData*) CreateSignature
{
    unsigned char sha_buffer[SHA_DIGEST_LENGTH];
    memset((void*) sha_buffer, 0, SHA_DIGEST_LENGTH);
    SHA1_Final(sha_buffer, &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, (const unsigned char*) sha_buffer, SHA_DIGEST_LENGTH);
    PKCS7_add_certificate(p7, mp_x509);    

    int c = 0;
    for ( ; 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);
    NSMutableData* signature = [NSMutableData data];
    unsigned char* p7Buf = (unsigned char*) malloc(p7Len);
    if (p7Buf != NULL) {
        unsigned char* pP7Buf = p7Buf;
        i2d_PKCS7(p7, &pP7Buf);
        [signature appendBytes: (const void*) p7Buf length: p7Len];
        free(p7Buf);
    }
    PKCS7_free(p7);
    
    return (signature);
}
- (SignatureHandler*) Clone
{
    return (self);
}
- (id) init: (NSString*) pfxfile password: (NSString*) password;
{
    self = [super init];

    FILE* fp = fopen([pfxfile cStringUsingEncoding: NSASCIIStringEncoding], "rb");
    if (fp == NULL)
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot open private key." userInfo: nil]);
    
    PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
    fclose(fp);
    
    if (p12 == NULL) 
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot parse private key." userInfo: nil]);

    mp_pkey = NULL;
    mp_x509 = NULL;
    mp_ca = NULL;
    int parseResult = PKCS12_parse(p12, [password cStringUsingEncoding: NSASCIIStringEncoding], &mp_pkey, &mp_x509, &mp_ca);
    PKCS12_free(p12);
    
    if (parseResult == 0)
        @throw ([NSException exceptionWithName: @"PDFNet Exception" reason: @"Cannot parse private key." userInfo: nil]);

    [self Reset];
    
    return (self);
}
- (void) dealloc
{
    sk_X509_free(mp_ca);
    X509_free(mp_x509);
    EVP_PKEY_free(mp_pkey);
    [super dealloc];
}
@end // implementation OpenSSLSignatureHandler

#endif // (!USE_STD_SIGNATURE_HANDLER)
////////// End of the OpenSSLSignatureHandler custom handler code. ////////////////////

NSString* input_path = @"../../TestFiles/";
NSString* output_path = @"../../TestFiles/Output/";

void CertifyPDF(NSString* in_docpath,
	NSString* in_cert_field_name,
	NSString* in_private_key_file_path,
	NSString* in_keyfile_password,
	NSString* in_appearance_image_path,
	NSString* in_outpath)
{
	NSLog(@"================================================================================");
	NSLog(@"Certifying PDF document");

	// Open an existing PDF
	PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

	NSLog(@"PDFDoc has %@", ([doc HasSignatures] ? @"signatures" : @"no signatures"));

	PTPage* page1 = [doc GetPage: 1];

	// Create a random text field that we can lock using the field permissions feature.
	PTTextWidget* annot1 = [PTTextWidget Create: doc pos: [[PTPDFRect alloc] initWithX1: 50 y1: 550 x2: 350 y2: 600] field_name: @"asdf_test_field"];
	[page1 AnnotPushBack: annot1];

	/* Create new signature form field in the PDFDoc. The name argument is optional;
	leaving it empty causes it to be auto-generated. However, you may need the name for later.
	Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
	PTDigitalSignatureField* certification_sig_field = [doc CreateDigitalSignatureField: in_cert_field_name];
	PTSignatureWidget* widgetAnnot = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 0 y1: 100 x2: 200 y2: 150] field: certification_sig_field];
	[page1 AnnotPushBack: widgetAnnot];

	// (OPTIONAL) Add an appearance.

	// Widget AP from image
	PTImage* img = [PTImage CreateWithFile: [doc GetSDFDoc] filename: in_appearance_image_path encoder_hints: [[PTObj alloc]init]];
	[widgetAnnot CreateSignatureAppearance: img];
	// End of optional appearance-adding code.

	// Add permissions. Lock the random text field.
	NSLog(@"Adding document permissions.");
	[certification_sig_field SetDocumentPermissions: e_ptannotating_formfilling_signing_allowed];
	NSLog(@"Adding field permissions.");
	NSMutableArray<NSString *> * fields_to_lock = [[NSMutableArray<NSString *> alloc] init];
	[fields_to_lock addObject:@"asdf_test_field"];
	[certification_sig_field SetFieldPermissions: e_ptdigsig_permission_include in_field_names: [fields_to_lock copy]];

#ifdef USE_STD_SIGNATURE_HANDLER
	[certification_sig_field CertifyOnNextSave: in_private_key_file_path in_password: in_keyfile_password];
#else
	OpenSSLSignatureHandler* sigHandler = [[OpenSSLSignatureHandler alloc] init:in_private_key_file_path password: in_keyfile_password];
	SignatureHandlerId sigHandlerId = [doc AddSignatureHandler: sigHandler];
	[certification_sig_field CertifyOnNextSaveWithCustomHandler: sigHandlerId];
#endif

	///// (OPTIONAL) Add more information to the signature dictionary.
	[certification_sig_field SetLocation: @"Vancouver, BC"];
	[certification_sig_field SetReason: @"Document certification."];
	[certification_sig_field SetContactInfo: @"www.pdftron.com"];
	///// End of optional sig info code.

	// Save the PDFDoc. Once the method below is called, PDFNetC will also sign the document using the information provided.
	[doc SaveToFile: in_outpath flags: 0];

	NSLog(@"================================================================================");
}

void SignPDF(NSString* in_docpath,
	NSString* in_approval_field_name,
	NSString* in_private_key_file_path,
	NSString* in_keyfile_password,
	NSString* in_appearance_img_path,
	NSString* in_outpath)
{
	NSLog(@"================================================================================");
	NSLog(@"Signing PDF document");

	// Open an existing PDF
	PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

	// Sign the approval signatures.
	PTField* found_approval_field = [doc GetField: in_approval_field_name];
	PTDigitalSignatureField* found_approval_signature_digsig_field = [[PTDigitalSignatureField alloc] initWithIn_field: found_approval_field];
	PTImage* img2 = [PTImage CreateWithFile: [doc GetSDFDoc] filename: in_appearance_img_path encoder_hints: [[PTObj alloc]init]];
	PTSignatureWidget* found_approval_signature_widget = [[PTSignatureWidget alloc] initWithD: [found_approval_field GetSDFObj]];
	[found_approval_signature_widget CreateSignatureAppearance: img2];

#ifdef USE_STD_SIGNATURE_HANDLER
	[found_approval_signature_digsig_field SignOnNextSave: in_private_key_file_path in_password: in_keyfile_password];
#else
	OpenSSLSignatureHandler* sigHandler = [[OpenSSLSignatureHandler alloc] init:in_private_key_file_path password: in_keyfile_password];
	PTSignatureHandlerId* sigHandlerId = [doc AddSignatureHandler: sigHandler];
	[found_approval_signature_digsig_field SignOnNextSaveWithCustomHandler: sigHandlerId];
#endif

	[doc SaveToFile: in_outpath flags: e_ptincremental];

	NSLog(@"================================================================================");
}

void ClearSignature(NSString* in_docpath,
	NSString* in_digsig_field_name,
	NSString* in_outpath)
{
	NSLog(@"================================================================================");
	NSLog(@"Clearing certification signature");

	PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];

	PTDigitalSignatureField* digsig = [[PTDigitalSignatureField alloc] initWithIn_field: [doc GetField: in_digsig_field_name]];
	
	NSLog(@"Clearing signature: %@", in_digsig_field_name);
	[digsig ClearSignature];

	if (![digsig HasCryptographicSignature])
	{
		NSLog(@"Cryptographic signature cleared properly.");
	}

	// Save incrementally so as to not invalidate other signatures' hashes from previous saves.
	[doc SaveToFile: in_outpath flags: e_ptincremental];

	NSLog(@"================================================================================");
}

void PrintSignaturesInfo(NSString* in_docpath)
{
	NSLog(@"================================================================================");
	NSLog(@"Reading and printing digital signature information");

	PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath: in_docpath];
	if (![doc HasSignatures])
	{
		NSLog(@"Doc has no signatures.");
		NSLog(@"================================================================================");
		return;
	}
	else
	{
		NSLog(@"Doc has signatures.");
	}

	
	PTFieldIterator* fitr = [doc GetFieldIterator];
	for(; [fitr HasNext]; [fitr Next]) 
	{
		NSLog(@"==========");
		if ([[fitr Current] IsLockedByDigitalSignature])
		{
			NSLog(@"Field locked by a digital signature");
		}
		else
		{
			NSLog(@"Field not locked by a digital signature");
		}

		NSLog(@"Field name: %@", [[fitr Current] GetName]);
		NSLog(@"==========");
	}

	NSLog(@"====================");
	NSLog(@"Now iterating over digital signatures only.");
	NSLog(@"====================");

	PTDigitalSignatureFieldIterator* digsig_fitr = [doc GetDigitalSignatureFieldIterator];
	for (; [digsig_fitr HasNext]; [digsig_fitr Next])
	{
		NSLog(@"==========");
		NSLog(@"Field name of digital signature: %@", [[[PTField alloc] initWithField_dict: [[digsig_fitr Current] GetSDFObj]] GetName]);

		PTDigitalSignatureField* digsigfield = [digsig_fitr Current];
		if (![digsigfield HasCryptographicSignature])
		{
			NSLog(@"Either digital signature field lacks a digital signature dictionary, "
				@"or digital signature dictionary lacks a cryptographic hash entry. "
				@"Digital signature field is not presently considered signed.");
			NSLog(@"==========");
			continue;
		}

		int cert_count = [digsigfield GetCertCount];
		NSLog(@"Cert count: %d", cert_count);
		for (int i = 0; i < cert_count; ++i)
		{
			NSData* cert = [digsigfield GetCert:i];
			NSLog(@"Cert #%d size: %lu", i, cert.length);
		}

		PTDigitalSignatureFieldSubFilterType subfilter = [digsigfield GetSubFilter];

		NSLog(@"Subfilter type: %d", (int)subfilter);

		if (subfilter != e_ptETSI_RFC3161)
		{
			NSLog(@"Signature's signer: %@", [digsigfield GetSignatureName] == NULL? @"" : [digsigfield GetSignatureName]);

			PTDate* signing_time = [digsigfield GetSigningTime];
			if ([signing_time IsValid])
			{
				NSLog(@"Signing day: %d", (int) [signing_time GetDay]);
			}

			NSLog(@"Location: %@", [digsigfield GetLocation] == NULL? @"" : [digsigfield GetLocation]);
			NSLog(@"Reason: %@", [digsigfield GetReason] == NULL? @"" : [digsigfield GetReason]);
			NSLog(@"Contact info: %@", [digsigfield GetContactInfo] == NULL? @"" : [digsigfield GetContactInfo]);
		}
		else
		{
			NSLog(@"SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)");
		}

		NSLog(([digsigfield HasVisibleAppearance]) ? @"Visible" : @"Not visible");

		PTDigitalSignatureFieldDocumentPermissions digsig_doc_perms = [digsigfield GetDocumentPermissions];
		NSArray<NSString *>* locked_fields = [digsigfield GetLockedFields];
		for (id field_name in locked_fields)
		{
			NSLog(@"This digital signature locks a field named: %@", field_name);
		}

		switch (digsig_doc_perms)
		{
		case e_ptno_changes_allowed:
			NSLog(@"No changes to the document can be made without invalidating this digital signature.");
			break;
		case e_ptformfilling_signing_allowed:
			NSLog(@"Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
			break;
		case e_ptannotating_formfilling_signing_allowed:
			NSLog(@"Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
			break;
		case e_ptunrestricted:
			NSLog(@"Document not restricted by this digital signature.");
			break;
		default:
			[NSException raise:@"Unrecognized digital signature document permission level." format:@"Unrecognized digital signature document permission level."];
		}
		NSLog(@"==========");
	}

	NSLog(@"================================================================================");
}

int main()
{
	@autoreleasepool {

		// Initialize PDFNetC
		[PTPDFNet Initialize: 0];
	

	#if (!USE_STD_SIGNATURE_HANDLER)
		// Initialize OpenSSL library
		CRYPTO_malloc_init();
		ERR_load_crypto_strings();
		OpenSSL_add_all_algorithms();
	#endif // (!USE_STD_SIGNATURE_HANDLER)

		int ret = 0;

		//////////////////// TEST 0: 
		/* Create an approval signature field that we can sign after certifying.
		(Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
		@try
		{
			PTPDFDoc* doc = [[PTPDFDoc alloc] initWithFilepath:[NSString stringWithFormat:@"%@%@", input_path, @"tiger.pdf"]];
			PTDigitalSignatureField* approval_signature_field = [doc CreateDigitalSignatureField: @"PDFTronApprovalSig"];
			PTSignatureWidget* widgetAnnotApproval = [PTSignatureWidget CreateWithDigitalSignatureField: doc pos: [[PTPDFRect alloc] initWithX1: 300 y1: 300 x2: 500 y2: 200] field: approval_signature_field];
			PTPage* page1 = [doc GetPage: 1];
			[page1 AnnotPushBack: widgetAnnotApproval];
			[doc SaveToFile: [NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_output.pdf"] flags: e_ptremove_unused];
		}
		@catch (NSException* e)
		{
			NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
			ret = 1;
		}

		//////////////////// TEST 1: certify a PDF.
		@try
		{
			CertifyPDF([NSString stringWithFormat:@"%@%@", input_path, @"tiger_withApprovalField.pdf"],
				@"PDFTronCertificationSig",
				[NSString stringWithFormat:@"%@%@", input_path, @"pdftron.pfx"],
				@"password",
				[NSString stringWithFormat:@"%@%@", input_path, @"pdftron.bmp"],
				[NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_output.pdf"]);
			PrintSignaturesInfo([NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_output.pdf"]);
		}
		@catch (NSException* e)
		{
			NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
			ret = 1;
		}

		//////////////////// TEST 2: sign a PDF with a certification and an unsigned signature field in it.
		@try
		{
			SignPDF([NSString stringWithFormat:@"%@%@", input_path, @"tiger_withApprovalField_certified.pdf"],
				@"PDFTronApprovalSig",
				[NSString stringWithFormat:@"%@%@", input_path, @"pdftron.pfx"],
				@"password",
				[NSString stringWithFormat:@"%@%@", input_path, @"signature.jpg"],
				[NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_approved_output.pdf"]);
			PrintSignaturesInfo([NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_approved_output.pdf"]);
		}
		@catch (NSException* e)
		{
			NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
			ret = 1;
		}

		//////////////////// TEST 3: Clear a certification from a document that is certified and has two approval signatures.
		@try
		{
			ClearSignature([NSString stringWithFormat:@"%@%@", input_path, @"tiger_withApprovalField_certified_approved.pdf"],
				@"PDFTronCertificationSig",
				[NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_approved_certcleared_output.pdf"]);
			PrintSignaturesInfo([NSString stringWithFormat:@"%@%@", output_path, @"tiger_withApprovalField_certified_approved_certcleared_output.pdf"]);
		}
		@catch (NSException* e)
		{
			NSLog(@"Exception: %@ - %@\n", e.name, e.reason);
			ret = 1;
		}

		//////////////////// End of tests. ////////////////////

		if (!ret)
		{
			NSLog(@"Tests successful.");
			NSLog(@"==========");
		}
		else
		{
			NSLog(@"Tests FAILED!!!");
			NSLog(@"==========");
		}

	#if (!USE_STD_SIGNATURE_HANDLER)
		ERR_free_strings();
		EVP_cleanup();
	#endif // (!USE_STD_SIGNATURE_HANDLER)

		return ret;
	}
}
close

Free Trial

Get unlimited trial usage of PDFTron SDK to bring accurate, reliable, and fast document processing capabilities to any application or workflow.

Select a platform to get started with your free trial.

Unlimited usage. No email address required.

PDFTron Receives USD$71 Million Growth Investment Led By Silversmith Capital Partners

Learn More
close