Digitally Sign PDF Files - Ruby Sample Code

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.

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