Digitally Sign PDF Files - Python Sample Code

Requirements
View Demo

Sample code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files; provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our Server SDK and PDF Digital Signature Library.

Implementation steps

To apply and validate digital signatures with Apryse Server SDK:

Step 1: Follow get started with Server SDK in your preferred language or framework
Step 2: Add the sample code provided in this guide. Make note of directions at top of code samples.

To use this feature in production, your license key will need the Digital Signature Package. Trial keys already include all packages.

1#!/usr/bin/python
2
3#-----------------------------------------------------------------------------------------------------------------------
4# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
5# Consult LICENSE.txt regarding license information.
6#-----------------------------------------------------------------------------------------------------------------------
7
8##----------------------------------------------------------------------------------------------------------------------
9## This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
10##
11## The following steps reflect typical intended usage of the digital signatures API:
12##
13## 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)).
14##
15## 1. EITHER:
16## (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
17## -OR-
18## (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
19## document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
20##
21## 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
22## If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
23## NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
24##
25## [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
26## or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
27## certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
28## is signed.]
29##
30## 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
31## a. Taking a PKCS #12 keyfile path and its password
32## b. Taking a buffer containing a PKCS #12 private keyfile and its password
33## c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
34## in the following fashion:
35## i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
36## validate/check a digital signature.
37## ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
38## pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
39## iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
40## NOTE: It is only possible to sign/certify one signature per call to the Save function.
41##
42## 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
43## IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
44## so as to not invalidate the other signature(s).
45##
46## Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
47## of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
48## add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
49##
50## Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
51## StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
52##----------------------------------------------------------------------------------------------------------------------
53
54
55import site
56site.addsitedir("../../../PDFNetC/Lib")
57import sys
58from PDFNetPython import *
59
60sys.path.append("../../LicenseKey/PYTHON")
61from LicenseKey import *
62
63def VerifySimple(in_docpath, in_public_key_file_path):
64 doc = PDFDoc(in_docpath)
65 print("==========")
66 opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)
67
68 # Add trust root to store of trusted certificates contained in VerificationOptions.
69 opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions.e_default_trust | VerificationOptions.e_certification_trust)
70
71 result = doc.VerifySignedDigitalSignatures(opts)
72
73 if result is PDFDoc.e_unsigned:
74 print("Document has no signed signature fields.")
75 return False
76 # e_failure == bad doc status, digest status, or permissions status
77 # (i.e. does not include trust issues, because those are flaky due to being network/config-related)
78 elif result is PDFDoc.e_failure:
79 print("Hard failure in verification on at least one signature.")
80 return False
81 elif result is PDFDoc.e_untrusted:
82 print("Could not verify trust for at least one signature.")
83 return False
84 elif result is PDFDoc.e_unsupported:
85 # If necessary, call GetUnsupportedFeatures on VerificationResult to check which
86 # unsupported features were encountered (requires verification using 'detailed' APIs)
87 print("At least one signature contains unsupported features.")
88 return False
89 # unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
90 elif result is PDFDoc.e_verified:
91 print("All signed signatures in document verified.")
92 return True
93 else:
94 print("unrecognized document verification status")
95 assert False, "unrecognized document status"
96
97
98def VerifyAllAndPrint(in_docpath, in_public_key_file_path):
99 doc = PDFDoc(in_docpath)
100 print("==========")
101 opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)
102
103 # Trust the public certificate we use for signing.
104 trusted_cert_buf = []
105 trusted_cert_file = MappedFile(in_public_key_file_path)
106 file_sz = trusted_cert_file.FileSize()
107 file_reader = FilterReader(trusted_cert_file)
108 trusted_cert_buf = file_reader.Read(file_sz)
109 opts.AddTrustedCertificate(trusted_cert_buf, len(trusted_cert_buf), VerificationOptions.e_default_trust | VerificationOptions.e_certification_trust)
110
111 # Iterate over the signatures and verify all of them.
112 digsig_fitr = doc.GetDigitalSignatureFieldIterator()
113 verification_status = True
114 while (digsig_fitr.HasNext()):
115 curr = digsig_fitr.Current()
116 result = curr.Verify(opts)
117 if result.GetVerificationStatus():
118 print("Signature verified, objnum: %lu" % curr.GetSDFObj().GetObjNum())
119 else:
120 print("Signature verification failed, objnum: %lu" % curr.GetSDFObj().GetObjNum())
121 verification_status = False
122
123 digest_algorithm = result.GetDigestAlgorithm()
124 if digest_algorithm is DigestAlgorithm.e_SHA1:
125 print("Digest algorithm: SHA-1")
126 elif digest_algorithm is DigestAlgorithm.e_SHA256:
127 print("Digest algorithm: SHA-256")
128 elif digest_algorithm is DigestAlgorithm.e_SHA384:
129 print("Digest algorithm: SHA-384")
130 elif digest_algorithm is DigestAlgorithm.e_SHA512:
131 print("Digest algorithm: SHA-512")
132 elif digest_algorithm is DigestAlgorithm.e_RIPEMD160:
133 print("Digest algorithm: RIPEMD-160")
134 elif digest_algorithm is DigestAlgorithm.e_unknown_digest_algorithm:
135 print("Digest algorithm: unknown")
136 else:
137 assert False, "unrecognized document status"
138
139 print("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s" % (
140 result.GetDocumentStatusAsString(),
141 result.GetDigestStatusAsString(),
142 result.GetTrustStatusAsString(),
143 result.GetPermissionsStatusAsString()))
144
145 changes = result.GetDisallowedChanges()
146 for it2 in changes:
147 print("\tDisallowed change: %s, objnum: %lu" % (it2.GetTypeAsString(), it2.GetObjNum()))
148
149 # Get and print all the detailed trust-related results, if they are available.
150 if result.HasTrustVerificationResult():
151 trust_verification_result = result.GetTrustVerificationResult()
152 print("Trust verified." if trust_verification_result.WasSuccessful() else "Trust not verifiable.")
153 print(trust_verification_result.GetResultString())
154
155 tmp_time_t = trust_verification_result.GetTimeOfTrustVerification()
156
157 trust_verification_time_enum = trust_verification_result.GetTimeOfTrustVerificationEnum()
158
159 if trust_verification_time_enum is VerificationOptions.e_current:
160 print("Trust verification attempted with respect to current time (as epoch time): " + str(tmp_time_t))
161 elif trust_verification_time_enum is VerificationOptions.e_signing:
162 print("Trust verification attempted with respect to signing time (as epoch time): " + str(tmp_time_t))
163 elif trust_verification_time_enum is VerificationOptions.e_timestamp:
164 print("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + str(tmp_time_t))
165 else:
166 assert False, "unrecognized time enum value"
167
168 if not trust_verification_result.GetCertPath():
169 print("Could not print certificate path.")
170 else:
171 print("Certificate path:")
172 cert_path = trust_verification_result.GetCertPath()
173 for j in range(len(cert_path)):
174 print("\tCertificate:")
175 full_cert = cert_path[j]
176 print("\t\tIssuer names:")
177 issuer_dn = full_cert.GetIssuerField().GetAllAttributesAndValues()
178 for i in range(len(issuer_dn)):
179 print("\t\t\t" + issuer_dn[i].GetStringValue())
180
181 print("\t\tSubject names:")
182 subject_dn = full_cert.GetSubjectField().GetAllAttributesAndValues()
183 for s in subject_dn:
184 print("\t\t\t" + s.GetStringValue())
185
186 print("\t\tExtensions:")
187 for x in full_cert.GetExtensions():
188 print("\t\t\t" + x.ToString())
189
190 else:
191 print("No detailed trust verification result available.")
192
193 unsupported_features = result.GetUnsupportedFeatures()
194 if not unsupported_features:
195 print("Unsupported features:")
196 for unsupported_feature in unsupported_features:
197 print("\t" + unsupported_feature)
198 print("==========")
199
200 digsig_fitr.Next()
201 return verification_status
202
203def CertifyPDF(in_docpath,
204 in_cert_field_name,
205 in_private_key_file_path,
206 in_keyfile_password,
207 in_appearance_image_path,
208 in_outpath):
209
210 print('================================================================================')
211 print('Certifying PDF document')
212
213 # Open an existing PDF
214 doc = PDFDoc(in_docpath)
215
216 if doc.HasSignatures():
217 print('PDFDoc has signatures')
218 else:
219 print('PDFDoc has no signatures')
220
221 page1 = doc.GetPage(1)
222
223 # Create a text field that we can lock using the field permissions feature.
224 annot1 = TextWidget.Create(doc, Rect(143, 440, 350, 460), "asdf_test_field")
225 page1.AnnotPushBack(annot1)
226
227 # Create a new signature form field in the PDFDoc. The name argument is optional;
228 # leaving it empty causes it to be auto-generated. However, you may need the name for later.
229 # Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
230 # Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
231 certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name)
232 widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), certification_sig_field)
233 page1.AnnotPushBack(widgetAnnot)
234
235 # (OPTIONAL) Add an appearance to the signature field.
236 img = Image.Create(doc.GetSDFDoc(), in_appearance_image_path)
237 widgetAnnot.CreateSignatureAppearance(img)
238
239 # Add permissions. Lock the random text field.
240 print('Adding document permissions.')
241 certification_sig_field.SetDocumentPermissions(DigitalSignatureField.e_annotating_formfilling_signing_allowed)
242
243 # Prepare to lock the text field that we created earlier.
244 print('Adding field permissions.')
245 certification_sig_field.SetFieldPermissions(DigitalSignatureField.e_include, ['asdf_test_field'])
246
247 certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password)
248
249 # (OPTIONAL) Add more information to the signature dictionary.
250 certification_sig_field.SetLocation('Vancouver, BC')
251 certification_sig_field.SetReason('Document certification.')
252 certification_sig_field.SetContactInfo('www.pdftron.com')
253
254 # Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
255 doc.Save(in_outpath, 0)
256
257 print('================================================================================')
258
259def SignPDF(in_docpath,
260 in_approval_field_name,
261 in_private_key_file_path,
262 in_keyfile_password,
263 in_appearance_img_path,
264 in_outpath):
265
266 print('================================================================================')
267 print('Signing PDF document')
268
269 # Open an existing PDF
270 doc = PDFDoc(in_docpath)
271
272 # Retrieve the unsigned approval signature field.
273 found_approval_field = doc.GetField(in_approval_field_name)
274 found_approval_signature_digsig_field = DigitalSignatureField(found_approval_field)
275
276 # (OPTIONAL) Add an appearance to the signature field.
277 img = Image.Create(doc.GetSDFDoc(), in_appearance_img_path)
278 found_approval_signature_widget = SignatureWidget(found_approval_field.GetSDFObj())
279 found_approval_signature_widget.CreateSignatureAppearance(img)
280
281 # Prepare the signature and signature handler for signing.
282 found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password)
283
284 # The actual approval signing will be done during the following incremental save operation.
285 doc.Save(in_outpath, SDFDoc.e_incremental)
286
287 print('================================================================================')
288
289def ClearSignature(in_docpath,
290 in_digsig_field_name,
291 in_outpath):
292
293 print('================================================================================')
294 print('Clearing certification signature')
295
296 doc = PDFDoc(in_docpath)
297
298 digsig = DigitalSignatureField(doc.GetField(in_digsig_field_name))
299
300 print('Clearing signature: ' + in_digsig_field_name)
301 digsig.ClearSignature()
302
303 if not digsig.HasCryptographicSignature():
304 print('Cryptographic signature cleared properly.')
305
306 # Save incrementally so as to not invalidate other signatures from previous saves.
307 doc.Save(in_outpath, SDFDoc.e_incremental)
308
309 print('================================================================================')
310
311def PrintSignaturesInfo(in_docpath):
312 print('================================================================================')
313 print('Reading and printing digital signature information')
314
315 doc = PDFDoc(in_docpath)
316 if not doc.HasSignatures():
317 print('Doc has no signatures.')
318 print('================================================================================')
319 return
320 else:
321 print('Doc has signatures.')
322
323 fitr = doc.GetFieldIterator()
324 while fitr.HasNext():
325 current = fitr.Current()
326 if (current.IsLockedByDigitalSignature()):
327 print("==========\nField locked by a digital signature")
328 else:
329 print("==========\nField not locked by a digital signature")
330
331 print('Field name: ' + current.GetName())
332 print('==========')
333
334 fitr.Next()
335
336 print("====================\nNow iterating over digital signatures only.\n====================")
337
338 digsig_fitr = doc.GetDigitalSignatureFieldIterator()
339 while digsig_fitr.HasNext():
340 current = digsig_fitr.Current()
341 print('==========')
342 print('Field name of digital signature: ' + Field(current.GetSDFObj()).GetName())
343
344 digsigfield = current
345 if not digsigfield.HasCryptographicSignature():
346 print("Either digital signature field lacks a digital signature dictionary, " +
347 "or digital signature dictionary lacks a cryptographic Contents entry. " +
348 "Digital signature field is not presently considered signed.\n" +
349 "==========")
350 digsig_fitr.Next()
351 continue
352
353 cert_count = digsigfield.GetCertCount()
354 print('Cert count: ' + str(cert_count))
355 for i in range(cert_count):
356 cert = digsigfield.GetCert(i)
357 print('Cert #' + i + ' size: ' + cert.length)
358
359 subfilter = digsigfield.GetSubFilter()
360
361 print('Subfilter type: ' + str(subfilter))
362
363 if subfilter is not DigitalSignatureField.e_ETSI_RFC3161:
364 print('Signature\'s signer: ' + digsigfield.GetSignatureName())
365
366 signing_time = digsigfield.GetSigningTime()
367 if signing_time.IsValid():
368 print('Signing time is valid.')
369
370 print('Location: ' + digsigfield.GetLocation())
371 print('Reason: ' + digsigfield.GetReason())
372 print('Contact info: ' + digsigfield.GetContactInfo())
373 else:
374 print('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)')
375
376 if digsigfield.HasVisibleAppearance():
377 print('Visible')
378 else:
379 print('Not visible')
380
381 digsig_doc_perms = digsigfield.GetDocumentPermissions()
382 locked_fields = digsigfield.GetLockedFields()
383 for it in locked_fields:
384 print('This digital signature locks a field named: ' + it)
385
386 if digsig_doc_perms is DigitalSignatureField.e_no_changes_allowed:
387 print('No changes to the document can be made without invalidating this digital signature.')
388 elif digsig_doc_perms is DigitalSignatureField.e_formfilling_signing_allowed:
389 print('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.')
390 elif digsig_doc_perms is DigitalSignatureField.e_annotating_formfilling_signing_allowed:
391 print('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.')
392 elif digsig_doc_perms is DigitalSignatureField.e_unrestricted:
393 print('Document not restricted by this digital signature.')
394 else:
395 print('Unrecognized digital signature document permission level.')
396 assert(False)
397 print('==========')
398 digsig_fitr.Next()
399
400 print('================================================================================')
401
402def CustomSigningAPI(doc_path,
403 cert_field_name,
404 private_key_file_path,
405 keyfile_password,
406 public_key_file_path,
407 appearance_image_path,
408 digest_algorithm_type,
409 PAdES_signing_mode,
410 output_path):
411 print('================================================================================')
412 print('Custom signing PDF document')
413
414 doc = PDFDoc(doc_path)
415
416 page1 = doc.GetPage(1)
417
418 digsig_field = doc.CreateDigitalSignatureField(cert_field_name)
419 widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), digsig_field)
420 page1.AnnotPushBack(widgetAnnot)
421
422 # (OPTIONAL) Add an appearance to the signature field.
423 img = Image.Create(doc.GetSDFDoc(), appearance_image_path)
424 widgetAnnot.CreateSignatureAppearance(img)
425
426 # Create a digital signature dictionary inside the digital signature field, in preparation for signing.
427 digsig_field.CreateSigDictForCustomSigning('Adobe.PPKLite',
428 DigitalSignatureField.e_ETSI_CAdES_detached if PAdES_signing_mode else DigitalSignatureField.e_adbe_pkcs7_detached,
429 7500) # For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be, in bytes.
430 # ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
431
432 # (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
433 current_date = Date()
434 current_date.SetCurrentTime()
435 digsig_field.SetSigDictTimeOfSigning(current_date)
436
437 doc.Save(output_path, SDFDoc.e_incremental)
438
439 # Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
440 pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type)
441
442 signer_cert = X509Certificate(public_key_file_path)
443
444 # Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
445 # The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
446 pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)
447
448 # Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
449 # The signedAttrs are certain attributes that become protected by their inclusion in the signature.
450 signedAttrs = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)
451
452 # Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
453 signedAttrs_digest = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs)
454
455 ############################ custom digest signing starts ############################
456 # At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
457 # which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
458 signature_value = DigestAlgorithm.SignDigest(
459 signedAttrs_digest,
460 digest_algorithm_type,
461 private_key_file_path,
462 keyfile_password)
463 ############################ custom digest signing ends ##############################
464
465 # Then, load all your chain certificates into a container of X509Certificate.
466 chain_certs = []
467
468 # Then, create ObjectIdentifiers for the algorithms you have used.
469 # Here we use digest_algorithm_type (SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
470 digest_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_SHA256)
471 signature_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_RSA_encryption_PKCS1)
472
473 # Then, put the CMS signature components together.
474 cms_signature = DigitalSignatureField.GenerateCMSSignature(
475 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
476 signature_value, signedAttrs)
477
478 # Write the signature to the document.
479 doc.SaveCustomSignature(cms_signature, digsig_field, output_path)
480
481 print('================================================================================')
482
483def TimestampAndEnableLTV(in_docpath,
484 in_tsa_url,
485 in_trusted_cert_path,
486 in_appearance_img_path,
487 in_outpath):
488 doc = PDFDoc(in_docpath)
489 doctimestamp_signature_field = doc.CreateDigitalSignatureField()
490 tst_config = TimestampingConfiguration(in_tsa_url)
491 opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)
492# It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
493# the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
494# response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
495# function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
496# in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.
497
498 opts.AddTrustedCertificate(in_trusted_cert_path)
499# By default, we only check online for revocation of certificates using the newer and lighter
500# OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
501# it may be necessary to enable online CRL revocation checking in order to verify some timestamps
502# (i.e. those that do not have an OCSP responder URL for all non-trusted certificates).
503
504 opts.EnableOnlineCRLRevocationChecking(True)
505
506 widgetAnnot = SignatureWidget.Create(doc, Rect(0.0, 100.0, 200.0, 150.0), doctimestamp_signature_field)
507 doc.GetPage(1).AnnotPushBack(widgetAnnot)
508
509 # (OPTIONAL) Add an appearance to the signature field.
510 img = Image.Create(doc.GetSDFDoc(), in_appearance_img_path)
511 widgetAnnot.CreateSignatureAppearance(img)
512
513 print('Testing timestamping configuration.')
514 config_result = tst_config.TestConfiguration(opts)
515 if config_result.GetStatus():
516 print('Success: timestamping configuration usable. Attempting to timestamp.')
517 else:
518 # Print details of timestamping failure.
519 print(config_result.GetString())
520 if config_result.HasResponseVerificationResult():
521 tst_result = config_result.GetResponseVerificationResult()
522 print('CMS digest status: '+ tst_result.GetCMSDigestStatusAsString())
523 print('Message digest status: ' + tst_result.GetMessageImprintDigestStatusAsString())
524 print('Trust status: ' + tst_result.GetTrustStatusAsString())
525 return False
526
527
528 doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts)
529
530 # Save/signing throws if timestamping fails.
531 doc.Save(in_outpath, SDFDoc.e_incremental)
532
533 print('Timestamping successful. Adding LTV information for DocTimeStamp signature.')
534
535 # Add LTV information for timestamp signature to document.
536 timestamp_verification_result = doctimestamp_signature_field.Verify(opts)
537 if not doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result):
538 print('Could not enable LTV for DocTimeStamp.')
539 return False
540 doc.Save(in_outpath, SDFDoc.e_incremental)
541 print('Added LTV information for DocTimeStamp signature successfully.')
542
543 return True
544
545def main():
546 # Initialize PDFNet
547 PDFNet.Initialize(LicenseKey)
548
549 result = True
550 input_path = '../../TestFiles/'
551 output_path = '../../TestFiles/Output/'
552
553 #################### TEST 0:
554 # Create an approval signature field that we can sign after certifying.
555 # (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
556 # Open an existing PDF
557 try:
558 doc = PDFDoc(input_path + 'waiver.pdf')
559
560 widgetAnnotApproval = SignatureWidget.Create(doc, Rect(300, 287, 376, 306), 'PDFTronApprovalSig')
561 page1 = doc.GetPage(1)
562 page1.AnnotPushBack(widgetAnnotApproval)
563 doc.Save(output_path + 'waiver_withApprovalField_output.pdf', SDFDoc.e_remove_unused)
564 except Exception as e:
565 print(e.args)
566 result = False
567 #################### TEST 1: certify a PDF.
568 try:
569 CertifyPDF(input_path + 'waiver_withApprovalField.pdf',
570 'PDFTronCertificationSig',
571 input_path + 'pdftron.pfx',
572 'password',
573 input_path + 'pdftron.bmp',
574 output_path + 'waiver_withApprovalField_certified_output.pdf')
575 PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_output.pdf')
576 except Exception as e:
577 print(e.args)
578 result = False
579 #################### TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
580 try:
581 SignPDF(input_path + 'waiver_withApprovalField_certified.pdf',
582 'PDFTronApprovalSig',
583 input_path + 'pdftron.pfx',
584 'password',
585 input_path + 'signature.jpg',
586 output_path + 'waiver_withApprovalField_certified_approved_output.pdf')
587 PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_output.pdf')
588 except Exception as e:
589 print(e.args)
590 result = False
591 #################### TEST 3: Clear a certification from a document that is certified and has an approval signature.
592 try:
593 ClearSignature(input_path + 'waiver_withApprovalField_certified_approved.pdf',
594 'PDFTronCertificationSig',
595 output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf')
596 PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf')
597 except Exception as e:
598 print(e.args)
599 result = False
600
601 #################### TEST 4: Verify a document's digital signatures.
602 try:
603 if not VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"):
604 result = False
605 except Exception as e:
606 print(e.args)
607 result = False
608
609 #################### TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
610 try:
611 if not VerifySimple(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'):
612 result = False
613 except Exception as e:
614 print(e.args)
615 result = False
616
617 #################### TEST 6: Custom signing API.
618 # The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
619 # which allows users to customize the process of signing documents. Among other things, this
620 # includes the capability to allow for easy integration of PDF-specific signing-related operations
621 # with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
622 # to system keystores, etc.
623 try:
624 CustomSigningAPI(input_path + "waiver.pdf",
625 "PDFTronApprovalSig",
626 input_path + "pdftron.pfx",
627 "password",
628 input_path + "pdftron.cer",
629 input_path + "signature.jpg",
630 DigestAlgorithm.e_SHA256,
631 True,
632 output_path + "waiver_custom_signed.pdf")
633 except Exception as e:
634 print(e.args)
635 result = False
636
637 #################### TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
638 # try:
639 # # Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
640 # # For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
641 # # Note that this url may not work in the future. A reliable solution requires using your own TSA.
642 # tsa_url = 'YOUR_URL_OF_TSA'
643 # if tsa_url == 'YOUR_URL_OF_TSA':
644 # raise Exception('Error: The URL of your timestamp authority was not specified.')
645 #
646 # # Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
647 # # For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
648 # # Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
649 # trusted_cert_path = 'YOUR_CERTIFICATE'
650 # if trusted_cert_path == 'YOUR_CERTIFICATE':
651 # raise Exception('Error: The path to your timestamp authority trusted root certificate was not specified.')
652 #
653 # if not TimestampAndEnableLTV(input_path + 'waiver.pdf',
654 # tsa_url,
655 # trusted_cert_path,
656 # input_path + 'signature.jpg',
657 # output_path+ 'waiver_DocTimeStamp_LTV.pdf'):
658 # result = False
659 # except Exception as e:
660 # print(e.args)
661 # result = False
662
663 #################### End of tests. #####################
664
665 if not result:
666 print("Tests FAILED!!!\n==========")
667 PDFNet.Terminate()
668 return
669 PDFNet.Terminate()
670 print("Tests successful.\n==========")
671
672if __name__ == '__main__':
673 main()
674# end if __name__ == '__main__'

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales