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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales