Sample C# code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files. Learn more about our Server SDK and PDF Digital Signature Library.
1//
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3//
4
5//----------------------------------------------------------------------------------------------------------------------
6// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
7//
8// The following steps reflect typical intended usage of the digital signatures API:
9//
10// 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)).
11//
12// 1. EITHER:
13// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
14// -OR-
15// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
16// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
17//
18// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
19// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
20// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
21//
22// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
23// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
24// certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
25// is signed.]
26//
27// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
28// a. Taking a PKCS #12 keyfile path and its password
29// b. Taking a buffer containing a PKCS #12 private keyfile and its password
30// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
31// in the following fashion:
32// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
33// validate/check a digital signature.
34// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
35// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
36// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
37// NOTE: It is only possible to sign/certify one signature per call to the Save function.
38//
39// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
40// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
41// so as to not invalidate the other signature(s).
42//
43// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
44// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
45// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
46//
47// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
48// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
49//----------------------------------------------------------------------------------------------------------------------
50
51// In order to use .NET Framework's Cryptography library, define "USE_DOTNET_CRYPTO" and then add System.Security to
52// references list.
53
54using System;
55using System.Collections.Generic;
56using System.IO;
57#if USE_DOTNET_CRYPTO
58using System.Security.Cryptography;
59using System.Security.Cryptography.Pkcs;
60using System.Security.Cryptography.X509Certificates;
61#endif // USE_DOTNET_CRYPTO
62
63using pdftron;
64using pdftron.Common;
65using pdftron.PDF;
66using pdftron.PDF.Annots;
67using pdftron.SDF;
68using pdftron.Crypto;
69
70namespace DigitalSignaturesTestCS
71{
72 //////////////////// Here follows an example of how to implement a custom signature handler. //////////
73#if USE_DOTNET_CRYPTO
74 class DotNetCryptoSignatureHandler : SignatureHandler
75 {
76 private List<byte> m_data;
77 private string m_signingCert;
78 private string m_certPassword;
79
80 public DotNetCryptoSignatureHandler(string signingCert, string password)
81 {
82 m_signingCert = signingCert;
83 m_certPassword = password;
84 m_data = new List<byte>();
85 }
86
87 public override void AppendData(byte[] data)
88 {
89 m_data.AddRange(data);
90 }
91
92 public override bool Reset()
93 {
94 m_data.Clear();
95 return (true);
96 }
97
98 public override byte[] CreateSignature()
99 {
100 try {
101 ContentInfo ci = new ContentInfo(m_data.ToArray());
102 SignedCms sc = new SignedCms(ci, true);
103 X509Certificate2 cert = new X509Certificate2(m_signingCert, m_certPassword);
104 CmsSigner cs = new CmsSigner();
105 cs.Certificate = cert;
106 cs.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");//SHA256
107 sc.ComputeSignature(cs);
108 byte[] sig = sc.Encode();
109 return (sig);
110 }
111 catch (Exception e) {
112 Console.Error.WriteLine(e);
113 }
114 return (null);
115 }
116
117 public override string GetName()
118 {
119 return ("Adobe.PPKLite");
120 }
121 }
122#endif // USE_DOTNET_CRYPTO
123 ////////// End of the DotNetCryptoSignatureHandler custom handler code. ////////////////////
124
125 class Class1
126 {
127 static string input_path = "../../../../TestFiles/";
128 static string output_path = "../../../../TestFiles/Output/";
129
130 static bool VerifySimple (string in_docpath, string in_public_key_file_path)
131 {
132 using (PDFDoc doc = new PDFDoc(in_docpath))
133 {
134 Console.WriteLine("==========");
135 VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);
136
137 // Add trust root to store of trusted certificates contained in VerificationOptions.
138 opts.AddTrustedCertificate(in_public_key_file_path,
139 (ushort)(VerificationOptions.CertificateTrustFlag.e_default_trust | VerificationOptions.CertificateTrustFlag.e_certification_trust));
140
141 PDFDoc.SignaturesVerificationStatus result = doc.VerifySignedDigitalSignatures(opts);
142 switch (result)
143 {
144 case PDFDoc.SignaturesVerificationStatus.e_unsigned:
145 Console.WriteLine("Document has no signed signature fields.");
146 return false;
147 /* e_failure == bad doc status, digest status, or permissions status
148 (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
149 case PDFDoc.SignaturesVerificationStatus.e_failure:
150 Console.WriteLine("Hard failure in verification on at least one signature.");
151 return false;
152 case PDFDoc.SignaturesVerificationStatus.e_untrusted:
153 Console.WriteLine("Could not verify trust for at least one signature.");
154 return false;
155 case PDFDoc.SignaturesVerificationStatus.e_unsupported:
156 /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
157 unsupported features were encountered (requires verification using 'detailed' APIs) */
158 Console.WriteLine("At least one signature contains unsupported features.");
159 return false;
160 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
161 case PDFDoc.SignaturesVerificationStatus.e_verified:
162 Console.WriteLine("All signed signatures in document verified.");
163 return true;
164 default:
165 throw new Exception("unrecognized document verification status");
166 }
167 }
168 }
169
170 static bool VerifyAllAndPrint(string in_docpath, string in_public_key_file_path)
171 {
172 using (PDFDoc doc = new PDFDoc(in_docpath))
173 {
174 Console.WriteLine("==========");
175 VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);
176
177 // Add trust root to store of trusted certificates contained in VerificationOptions.
178 opts.AddTrustedCertificate(in_public_key_file_path,
179 (ushort) (VerificationOptions.CertificateTrustFlag.e_default_trust | VerificationOptions.CertificateTrustFlag.e_certification_trust));
180
181 // Iterate over the signatures and verify all of them.
182 DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
183 bool verification_status = true;
184 for (; digsig_fitr.HasNext(); digsig_fitr.Next())
185 {
186 DigitalSignatureField curr = digsig_fitr.Current();
187 VerificationResult result = curr.Verify(opts);
188 if (result.GetVerificationStatus())
189 {
190 Console.Write("Signature verified, ");
191 }
192 else
193 {
194 Console.Write("Signature verification failed, ");
195 verification_status = false;
196 }
197 Console.WriteLine("objnum: {0}", curr.GetSDFObj().GetObjNum());
198
199 switch (result.GetDigestAlgorithm())
200 {
201 case DigestAlgorithm.Type.e_sha1:
202 Console.WriteLine("Digest algorithm: SHA-1");
203 break;
204 case DigestAlgorithm.Type.e_sha256:
205 Console.WriteLine("Digest algorithm: SHA-256");
206 break;
207 case DigestAlgorithm.Type.e_sha384:
208 Console.WriteLine("Digest algorithm: SHA-384");
209 break;
210 case DigestAlgorithm.Type.e_sha512:
211 Console.WriteLine("Digest algorithm: SHA-512");
212 break;
213 case DigestAlgorithm.Type.e_ripemd160:
214 Console.WriteLine("Digest algorithm: RIPEMD-160");
215 break;
216 case DigestAlgorithm.Type.e_unknown_digest_algorithm:
217 Console.WriteLine("Digest algorithm: unknown");
218 break;
219 default:
220 throw new Exception("unrecognized digest algorithm");
221 }
222 Console.WriteLine("Detailed verification result: \n\t{0}\n\t{1}\n\t{2}\n\t{3}",
223 result.GetDocumentStatusAsString(),
224 result.GetDigestStatusAsString(),
225 result.GetTrustStatusAsString(),
226 result.GetPermissionsStatusAsString());
227
228
229 DisallowedChange[] changes = result.GetDisallowedChanges();
230 foreach (DisallowedChange it2 in changes)
231 {
232 Console.WriteLine("\tDisallowed change: {0}, objnum: {1}", it2.GetTypeAsString(), it2.GetObjNum());
233 }
234
235 // Get and print all the detailed trust-related results, if they are available.
236 if (result.HasTrustVerificationResult())
237 {
238 TrustVerificationResult trust_verification_result = result.GetTrustVerificationResult();
239 Console.WriteLine(trust_verification_result.WasSuccessful() ? "Trust verified." : "Trust not verifiable.");
240 Console.WriteLine(trust_verification_result.GetResultString());
241
242 long time_of_verification = trust_verification_result.GetTimeOfTrustVerification();
243 switch (trust_verification_result.GetTimeOfTrustVerificationEnum())
244 {
245 case VerificationOptions.TimeMode.e_current:
246 Console.WriteLine("Trust verification attempted with respect to current time (as epoch time): {0}", time_of_verification);
247 break;
248 case VerificationOptions.TimeMode.e_signing:
249 Console.WriteLine("Trust verification attempted with respect to signing time (as epoch time): {0}", time_of_verification);
250 break;
251 case VerificationOptions.TimeMode.e_timestamp:
252 Console.WriteLine("Trust verification attempted with respect to secure embedded timestamp (as epoch time): {0}", time_of_verification);
253 break;
254 default:
255 throw new Exception("unrecognized time enum value");
256 }
257
258 if (trust_verification_result.GetCertPath().Length == 0)
259 {
260 Console.WriteLine("Could not print certificate path.");
261 }
262 else
263 {
264 Console.WriteLine("Certificate path:");
265 X509Certificate[] cert_path = trust_verification_result.GetCertPath();
266 for (int j = 0; j < cert_path.Length; j++)
267 {
268 Console.WriteLine("\tCertificate:");
269 X509Certificate full_cert = cert_path[j];
270 Console.WriteLine("\t\tIssuer names:");
271 X501AttributeTypeAndValue[] issuer_dn = full_cert.GetIssuerField().GetAllAttributesAndValues();
272 for (int i = 0; i < issuer_dn.Length; i++)
273 {
274 Console.WriteLine("\t\t\t" + issuer_dn[i].GetStringValue());
275 }
276 Console.WriteLine("\t\tSubject names:");
277 X501AttributeTypeAndValue[] subject_dn = full_cert.GetSubjectField().GetAllAttributesAndValues();
278 for (int i = 0; i < subject_dn.Length; i++)
279 {
280 Console.WriteLine("\t\t\t" + subject_dn[i].GetStringValue());
281 }
282 Console.WriteLine("\t\tExtensions:");
283 for (int i = 0; i < full_cert.GetExtensions().Length; i++)
284 {
285 Console.WriteLine("\t\t\t" + full_cert.GetExtensions()[i].ToString());
286 }
287 }
288 }
289 }
290 else
291 {
292 Console.WriteLine("No detailed trust verification result available.");
293 }
294
295 string[] unsupported_features = result.GetUnsupportedFeatures();
296 if (unsupported_features.Length > 0)
297 {
298 Console.WriteLine("Unsupported features:");
299
300 for (int i = 0; i < unsupported_features.Length; i++)
301 {
302 Console.WriteLine("\t" + unsupported_features[i]);
303 }
304 }
305 Console.WriteLine("==========");
306 }
307
308 return verification_status;
309 }
310 }
311
312 static void CertifyPDF(string in_docpath,
313 string in_cert_field_name,
314 string in_private_key_file_path,
315 string in_keyfile_password,
316 string in_appearance_image_path,
317 string in_outpath)
318 {
319 Console.Out.WriteLine("================================================================================");
320 Console.Out.WriteLine("Certifying PDF document");
321
322 // Open an existing PDF
323 using (PDFDoc doc = new PDFDoc(in_docpath))
324 {
325 Console.Out.WriteLine("PDFDoc has " + (doc.HasSignatures() ? "signatures" : "no signatures"));
326
327 Page page1 = doc.GetPage(1);
328
329 // Create a text field that we can lock using the field permissions feature.
330 TextWidget annot1 = TextWidget.Create(doc, new Rect(143, 440, 350, 460), "asdf_test_field");
331 page1.AnnotPushBack(annot1);
332
333 /* Create a new signature form field in the PDFDoc. The name argument is optional;
334 leaving it empty causes it to be auto-generated. However, you may need the name for later.
335 Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
336 Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
337 DigitalSignatureField certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name);
338 SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(143, 287, 219, 306), certification_sig_field);
339 page1.AnnotPushBack(widgetAnnot);
340
341 // (OPTIONAL) Add an appearance to the signature field.
342 Image img = Image.Create(doc, in_appearance_image_path);
343 widgetAnnot.CreateSignatureAppearance(img);
344
345 // Prepare the document locking permission level. It will be applied upon document certification.
346 Console.Out.WriteLine("Adding document permissions.");
347 certification_sig_field.SetDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
348
349 // Prepare to lock the text field that we created earlier.
350 Console.Out.WriteLine("Adding field permissions.");
351 string[] fields_to_lock = new string[1];
352 fields_to_lock[0] = "asdf_test_field";
353 certification_sig_field.SetFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);
354
355 #if USE_DOTNET_CRYPTO
356 DotNetCryptoSignatureHandler sigHandler = new DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password);
357 SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
358 certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId);
359 /* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format
360 for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format,
361 so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
362 when using the StdSignatureHandler. */
363 Obj f_obj = certification_sig_field.GetSDFObj();
364 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
365 #else
366 certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password);
367 #endif
368
369 // (OPTIONAL) Add more information to the signature dictionary.
370 certification_sig_field.SetLocation("Vancouver, BC");
371 certification_sig_field.SetReason("Document certification.");
372 certification_sig_field.SetContactInfo("www.pdftron.com");
373
374 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
375 doc.Save(in_outpath, 0);
376 }
377
378 Console.Out.WriteLine("================================================================================");
379 }
380
381 static void SignPDF(string in_docpath,
382 string in_approval_field_name,
383 string in_private_key_file_path,
384 string in_keyfile_password,
385 string in_appearance_img_path,
386 string in_outpath)
387 {
388 Console.Out.WriteLine("================================================================================");
389 Console.Out.WriteLine("Signing PDF document");
390
391 // Open an existing PDF
392 using (PDFDoc doc = new PDFDoc(in_docpath))
393 {
394 // Retrieve the unsigned approval signature field.
395 Field found_approval_field = doc.GetField(in_approval_field_name);
396 DigitalSignatureField found_approval_signature_digsig_field = new DigitalSignatureField(found_approval_field);
397
398 // (OPTIONAL) Add an appearance to the signature field.
399 Image img = Image.Create(doc, in_appearance_img_path);
400 SignatureWidget found_approval_signature_widget = new SignatureWidget(found_approval_field.GetSDFObj());
401 found_approval_signature_widget.CreateSignatureAppearance(img);
402
403 // Prepare the signature and signature handler for signing.
404 #if USE_DOTNET_CRYPTO
405 DotNetCryptoSignatureHandler sigHandler = new DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password);
406 SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
407 found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId);
408 /* Add a SubFilter name that uniquely identifies the signature format for verification tools. As an
409 example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, so we embed
410 one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
411 when using the StdSignatureHandler.*/
412 Obj f_obj = found_approval_signature_digsig_field.GetSDFObj();
413 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
414 #else
415 found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password);
416 #endif
417
418 // The actual approval signing will be done during the following incremental save operation.
419 doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental);
420 }
421 Console.Out.WriteLine("================================================================================");
422 }
423
424 static void ClearSignature(string in_docpath,
425 string in_digsig_field_name,
426 string in_outpath)
427 {
428 Console.Out.WriteLine("================================================================================");
429 Console.Out.WriteLine("Clearing certification signature");
430
431 using (PDFDoc doc = new PDFDoc(in_docpath))
432 {
433 DigitalSignatureField digsig = new DigitalSignatureField(doc.GetField(in_digsig_field_name));
434
435 Console.Out.WriteLine("Clearing signature: " + in_digsig_field_name);
436 digsig.ClearSignature();
437
438 if (!digsig.HasCryptographicSignature())
439 {
440 Console.Out.WriteLine("Cryptographic signature cleared properly.");
441 }
442
443 // Save incrementally so as to not invalidate other signatures from previous saves.
444 doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental);
445 }
446
447 Console.Out.WriteLine("================================================================================");
448 }
449
450 static void PrintSignaturesInfo(string in_docpath)
451 {
452 Console.Out.WriteLine("================================================================================");
453 Console.Out.WriteLine("Reading and printing digital signature information");
454
455 using (PDFDoc doc = new PDFDoc(in_docpath))
456 {
457 if (!doc.HasSignatures())
458 {
459 Console.Out.WriteLine("Doc has no signatures.");
460 Console.Out.WriteLine("================================================================================");
461 return;
462 }
463 else
464 {
465 Console.Out.WriteLine("Doc has signatures.");
466 }
467
468
469 for (FieldIterator fitr = doc.GetFieldIterator(); fitr.HasNext(); fitr.Next())
470 {
471 if (fitr.Current().IsLockedByDigitalSignature())
472 {
473 Console.Out.WriteLine("==========\nField locked by a digital signature");
474 }
475 else
476 {
477 Console.Out.WriteLine("==========\nField not locked by a digital signature");
478 }
479
480 Console.Out.WriteLine("Field name: " + fitr.Current().GetName());
481 Console.Out.WriteLine("==========");
482 }
483
484 Console.Out.WriteLine("====================\nNow iterating over digital signatures only.\n====================");
485
486 DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
487 for (; digsig_fitr.HasNext(); digsig_fitr.Next())
488 {
489 Console.Out.WriteLine("==========");
490 Console.Out.WriteLine("Field name of digital signature: " + new Field(digsig_fitr.Current().GetSDFObj()).GetName());
491
492 DigitalSignatureField digsigfield = digsig_fitr.Current();
493 if (!digsigfield.HasCryptographicSignature())
494 {
495 Console.Out.WriteLine("Either digital signature field lacks a digital signature dictionary, " +
496 "or digital signature dictionary lacks a cryptographic Contents entry. " +
497 "Digital signature field is not presently considered signed.\n" +
498 "==========");
499 continue;
500 }
501
502 int cert_count = digsigfield.GetCertCount();
503 Console.Out.WriteLine("Cert count: " + cert_count);
504 for (int i = 0; i < cert_count; ++i)
505 {
506 byte[] cert = digsigfield.GetCert(i);
507 Console.Out.WriteLine("Cert #" + i + " size: " + cert.Length);
508 }
509
510 DigitalSignatureField.SubFilterType subfilter = digsigfield.GetSubFilter();
511
512 Console.Out.WriteLine("Subfilter type: " + (int)subfilter);
513
514 if (subfilter != DigitalSignatureField.SubFilterType.e_ETSI_RFC3161)
515 {
516 Console.Out.WriteLine("Signature's signer: " + digsigfield.GetSignatureName());
517
518 Date signing_time = digsigfield.GetSigningTime();
519 if (signing_time.IsValid())
520 {
521 Console.Out.WriteLine("Signing time is valid.");
522 }
523
524 Console.Out.WriteLine("Location: " + digsigfield.GetLocation());
525 Console.Out.WriteLine("Reason: " + digsigfield.GetReason());
526 Console.Out.WriteLine("Contact info: " + digsigfield.GetContactInfo());
527 }
528 else
529 {
530 Console.Out.WriteLine("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)\n");
531 }
532
533 Console.Out.WriteLine(((digsigfield.HasVisibleAppearance()) ? "Visible" : "Not visible"));
534
535 DigitalSignatureField.DocumentPermissions digsig_doc_perms = digsigfield.GetDocumentPermissions();
536 string[] locked_fields = digsigfield.GetLockedFields();
537 foreach (string field_name in locked_fields)
538 {
539 Console.Out.WriteLine("This digital signature locks a field named: " + field_name);
540 }
541
542 switch (digsig_doc_perms)
543 {
544 case DigitalSignatureField.DocumentPermissions.e_no_changes_allowed:
545 Console.Out.WriteLine("No changes to the document can be made without invalidating this digital signature.");
546 break;
547 case DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed:
548 Console.Out.WriteLine("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
549 break;
550 case DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed:
551 Console.Out.WriteLine("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
552 break;
553 case DigitalSignatureField.DocumentPermissions.e_unrestricted:
554 Console.Out.WriteLine("Document not restricted by this digital signature.");
555 break;
556 default:
557 throw new Exception("Unrecognized digital signature document permission level.");
558 }
559 Console.Out.WriteLine("==========");
560 }
561 }
562
563 Console.Out.WriteLine("================================================================================");
564 }
565
566 static void CustomSigningAPI(string doc_path,
567 string cert_field_name,
568 string private_key_file_path,
569 string keyfile_password,
570 string public_key_file_path,
571 string appearance_image_path,
572 DigestAlgorithm.Type digest_algorithm_type,
573 bool PAdES_signing_mode,
574 string output_path)
575 {
576 Console.Out.WriteLine("================================================================================");
577 Console.Out.WriteLine("Custom signing PDF document");
578 using (PDFDoc doc = new PDFDoc(doc_path))
579 {
580 Page page1 = doc.GetPage(1);
581
582 DigitalSignatureField digsig_field = doc.CreateDigitalSignatureField(cert_field_name);
583 SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(143, 287, 219, 306), digsig_field);
584 page1.AnnotPushBack(widgetAnnot);
585
586 // (OPTIONAL) Add an appearance to the signature field.
587 Image img = Image.Create(doc, appearance_image_path);
588 widgetAnnot.CreateSignatureAppearance(img);
589
590 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
591 digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
592 PAdES_signing_mode ? DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached : DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached,
593 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.
594 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
595
596 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
597 Date current_date = new Date();
598 current_date.SetCurrentTime();
599 digsig_field.SetSigDictTimeOfSigning(current_date);
600
601 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);
602
603 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
604 byte[] pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type);
605
606 X509Certificate signer_cert = new X509Certificate(public_key_file_path);
607
608 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
609 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
610 byte[] pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);
611
612 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
613 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
614 byte[] signedAttrs = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute);
615
616 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
617 byte[] signedAttrs_digest = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs);
618
619 //////////////////////////// custom digest signing starts ////////////////////////////
620 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
621 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
622 byte[] signature_value = DigestAlgorithm.SignDigest(
623 signedAttrs_digest,
624 digest_algorithm_type,
625 private_key_file_path,
626 keyfile_password);
627 //////////////////////////// custom digest signing ends //////////////////////////////
628
629 // Then, load all your chain certificates into a container of X509Certificate.
630 X509Certificate[] chain_certs = {};
631
632 // Then, create ObjectIdentifiers for the algorithms you have used.
633 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
634 ObjectIdentifier digest_algorithm_oid = new ObjectIdentifier(digest_algorithm_type);
635 ObjectIdentifier signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1);
636
637 // Then, put the CMS signature components together.
638 byte[] cms_signature = DigitalSignatureField.GenerateCMSSignature(
639 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
640 signature_value, signedAttrs);
641
642 // Write the signature to the document.
643 doc.SaveCustomSignature(cms_signature, digsig_field, output_path);
644 }
645 Console.Out.WriteLine("================================================================================");
646 }
647
648 static bool TimestampAndEnableLTV(string doc_path,
649 string tsa_url,
650 string trusted_cert_path,
651 string appearance_img_path,
652 string output_path)
653 {
654 using (PDFDoc doc = new PDFDoc(doc_path))
655 {
656 DigitalSignatureField doctimestamp_signature_field = doc.CreateDigitalSignatureField();
657 TimestampingConfiguration tst_config = new TimestampingConfiguration(tsa_url);
658 VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);
659 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
660 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
661 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
662 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
663 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
664 opts.AddTrustedCertificate(trusted_cert_path);
665 /* By default, we only check online for revocation of certificates using the newer and lighter
666 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
667 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
668 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
669 opts.EnableOnlineCRLRevocationChecking(true);
670
671 SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(0, 100, 200, 150), doctimestamp_signature_field);
672 doc.GetPage(1).AnnotPushBack(widgetAnnot);
673 Obj widgetObj = widgetAnnot.GetSDFObj();
674
675 // (OPTIONAL) Add an appearance to the signature field.
676 Image img = Image.Create(doc, appearance_img_path);
677 widgetAnnot.CreateSignatureAppearance(img);
678
679 Console.WriteLine("Testing timestamping configuration.");
680 TimestampingResult config_result = tst_config.TestConfiguration(opts);
681 if (config_result.GetStatus())
682 {
683 Console.WriteLine("Success: timestamping configuration usable. Attempting to timestamp.");
684 }
685 else
686 {
687 // Print details of timestamping failure.
688 Console.WriteLine(config_result.GetString());
689 if (config_result.HasResponseVerificationResult())
690 {
691 EmbeddedTimestampVerificationResult tst_result = config_result.GetResponseVerificationResult();
692 Console.WriteLine("CMS digest status: {0}\n", tst_result.GetCMSDigestStatusAsString());
693 Console.WriteLine("Message digest status:{0}\n", tst_result.GetMessageImprintDigestStatusAsString());
694 Console.WriteLine("Trust status: {0}\n", tst_result.GetTrustStatusAsString());
695 }
696 return false;
697 }
698
699 doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts);
700
701 // Save/signing throws if timestamping fails.
702 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);
703
704 Console.WriteLine("Timestamping successful. Adding LTV information for DocTimeStamp signature.");
705
706 // Add LTV information for timestamp signature to document.
707 VerificationResult timestamp_verification_result = doctimestamp_signature_field.Verify(opts);
708 if (!doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result))
709 {
710 Console.WriteLine("Could not enable LTV for DocTimeStamp.");
711 return false;
712 }
713 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);
714 Console.WriteLine("Added LTV information for DocTimeStamp signature successfully.");
715
716 return true;
717 }
718 }
719
720 private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
721 static Class1() {}
722
723 /// <summary>
724 /// The main entry point for the application.
725 /// </summary>
726 [STAThread]
727 static void Main(string[] args)
728 {
729 // Initialize PDFNetC
730 PDFNet.Initialize(PDFTronLicense.Key);
731
732 bool result = true;
733
734 //////////////////// TEST 0:
735 /* Create an approval signature field that we can sign after certifying.
736 (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
737 try
738 {
739 using (PDFDoc doc = new PDFDoc(input_path + "waiver.pdf"))
740 {
741 DigitalSignatureField approval_signature_field = doc.CreateDigitalSignatureField("PDFTronApprovalSig");
742 SignatureWidget widgetAnnotApproval = SignatureWidget.Create(doc, new Rect(300, 287, 376, 306), approval_signature_field);
743 Page page1 = doc.GetPage(1);
744 page1.AnnotPushBack(widgetAnnotApproval);
745 doc.Save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc.SaveOptions.e_remove_unused);
746 }
747 }
748 catch (Exception e)
749 {
750 Console.Error.WriteLine(e);
751 result = false;
752 }
753
754 //////////////////// TEST 1: certify a PDF.
755 try
756 {
757 CertifyPDF(input_path + "waiver_withApprovalField.pdf",
758 "PDFTronCertificationSig",
759 input_path + "pdftron.pfx",
760 "password",
761 input_path + "pdftron.bmp",
762 output_path + "waiver_withApprovalField_certified_output.pdf");
763 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
764 }
765 catch (Exception e)
766 {
767 Console.Error.WriteLine(e);
768 result = false;
769 }
770
771 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
772 try
773 {
774 SignPDF(input_path + "waiver_withApprovalField_certified.pdf",
775 "PDFTronApprovalSig",
776 input_path + "pdftron.pfx",
777 "password",
778 input_path + "signature.jpg",
779 output_path + "waiver_withApprovalField_certified_approved_output.pdf");
780 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
781 }
782 catch (Exception e)
783 {
784 Console.Error.WriteLine(e);
785 result = false;
786 }
787
788 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
789 try
790 {
791 ClearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
792 "PDFTronCertificationSig",
793 output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
794 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
795 }
796 catch (Exception e)
797 {
798 Console.Error.WriteLine(e);
799 result = false;
800 }
801
802 //////////////////// TEST 4: Verify a document's digital signatures.
803 try
804 {
805 if (!VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf",
806 input_path + "pdftron.cer"))
807 {
808 result = false;
809 }
810 }
811 catch (Exception e)
812 {
813 Console.Error.WriteLine(e);
814 result = false;
815 }
816 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
817 try
818 {
819 if (!VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf",
820 input_path + "pdftron.cer"))
821 {
822 result = false;
823 }
824 }
825 catch (Exception e)
826 {
827 Console.Error.WriteLine(e);
828 result = false;
829 }
830
831 //////////////////// TEST 6: Custom signing API.
832 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
833 // which allows users to customize the process of signing documents. Among other things, this
834 // includes the capability to allow for easy integration of PDF-specific signing-related operations
835 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
836 // to system keystores, etc.
837 try
838 {
839 CustomSigningAPI(input_path + "waiver.pdf",
840 "PDFTronApprovalSig",
841 input_path + "pdftron.pfx",
842 "password",
843 input_path + "pdftron.cer",
844 input_path + "signature.jpg",
845 DigestAlgorithm.Type.e_sha256,
846 true,
847 output_path + "waiver_custom_signed.pdf");
848 }
849 catch (Exception e)
850 {
851 Console.Error.WriteLine(e);
852 result = false;
853 }
854
855 //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
856 // try
857 // {
858 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
859 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
860 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
861 // string tsa_url = "YOUR_URL_OF_TSA";
862 // if (tsa_url == "YOUR_URL_OF_TSA")
863 // {
864 // throw new Exception("Error: The URL of your timestamp authority was not specified.");
865 // }
866 //
867 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
868 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
869 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
870 // string trusted_cert_path = "YOUR_CERTIFICATE";
871 // if (trusted_cert_path == "YOUR_CERTIFICATE")
872 // {
873 // throw new Exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
874 // }
875 //
876 // if (!TimestampAndEnableLTV(input_path + "waiver.pdf",
877 // tsa_url,
878 // trusted_cert_path,
879 // input_path + "signature.jpg",
880 // output_path+ "waiver_DocTimeStamp_LTV.pdf"))
881 // {
882 // result = false;
883 // }
884 // }
885 // catch (Exception e)
886 // {
887 // Console.Error.WriteLine(e);
888 // result = false;
889 // }
890
891 //////////////////// End of tests. ////////////////////
892 PDFNet.Terminate();
893 if (result)
894 {
895 Console.Out.WriteLine("Tests successful.\n==========");
896 }
897 else
898 {
899 Console.Out.WriteLine("Tests FAILED!!!\n==========");
900 }
901 }
902 }
903}
1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult LICENSE.txt regarding license information.
4//---------------------------------------------------------------------------------------
5
6////----------------------------------------------------------------------------------------------------------------------
7//// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
8////
9//// The following steps reflect typical intended usage of the digital signatures API:
10////
11//// 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)).
12////
13//// 1. EITHER:
14//// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
15//// -OR-
16//// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
17//// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
18////
19//// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
20//// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
21//// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
22////
23//// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
24//// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
25//// certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
26//// is signed.]
27////
28//// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
29//// a. Taking a PKCS //12 keyfile path and its password
30//// b. Taking a buffer containing a PKCS //12 private keyfile and its password
31//// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
32//// in the following fashion:
33//// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
34//// validate/check a digital signature.
35//// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
36//// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
37//// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
38//// NOTE: It is only possible to sign/certify one signature per call to the Save function.
39////
40//// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
41//// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
42//// so as to not invalidate the other signature(s).
43////
44//// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
45//// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
46//// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
47////
48//// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
49//// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
50////----------------------------------------------------------------------------------------------------------------------
51
52package main
53import (
54 "fmt"
55 "strconv"
56 "flag"
57 "testing"
58 . "github.com/ApryseSDK/pdftron-go/v2"
59)
60
61var licenseKey string
62var modulePath string
63
64func init() {
65 flag.StringVar(&licenseKey, "license", "", "License key for Apryse SDK")
66 flag.StringVar(&modulePath, "modulePath", "", "Module path for Apryse SDK")
67}
68
69func VerifySimple(inDocpath string, inPublicKeyFilePath string) bool{
70 doc := NewPDFDoc(inDocpath)
71 fmt.Println("==========")
72 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
73
74 // Add trust root to store of trusted certificates contained in VerificationOptions.
75 opts.AddTrustedCertificate(inPublicKeyFilePath, uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))
76
77 result := doc.VerifySignedDigitalSignatures(opts)
78
79 if result == PDFDocE_unsigned{
80 fmt.Println("Document has no signed signature fields.")
81 return false
82 // e_failure == bad doc status, digest status, or permissions status
83 // (i.e. does not include trust issues, because those are flaky due to being network/config-related)
84 }else if result == PDFDocE_failure{
85 fmt.Println("Hard failure in verification on at least one signature.")
86 return false
87 }else if result == PDFDocE_untrusted{
88 fmt.Println("Could not verify trust for at least one signature.")
89 return false
90 }else if result == PDFDocE_unsupported{
91 // If necessary, call GetUnsupportedFeatures on VerificationResult to check which
92 // unsupported features were encountered (requires verification using 'detailed' APIs)
93 fmt.Println("At least one signature contains unsupported features.")
94 return false
95 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
96 }else if result == PDFDocE_verified{
97 fmt.Println("All signed signatures in document verified.")
98 return true
99 }else{
100 fmt.Println("unrecognized document verification status")
101 return false
102 }
103}
104
105func VerifyAllAndPrint(inDocpath string, inPublicKeyFilePath string) bool{
106 doc := NewPDFDoc(inDocpath)
107 fmt.Println("==========")
108 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
109
110 // Trust the public certificate we use for signing.
111 trustedCertFile := NewMappedFile(inPublicKeyFilePath)
112 fileSz := trustedCertFile.FileSize()
113 fileReader := NewFilterReader(trustedCertFile)
114 trustedCertBuf := fileReader.Read(fileSz)
115 trustedCertBytes := make([]byte, int(trustedCertBuf.Size()))
116 for i := 0; i < int(trustedCertBuf.Size()); i ++ {
117 trustedCertBytes[i] = trustedCertBuf.Get(i)
118 }
119 opts.AddTrustedCertificate(&trustedCertBytes[0], int64(len(trustedCertBytes)), uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))
120
121 // Iterate over the signatures and verify all of them.
122 digsigFitr := doc.GetDigitalSignatureFieldIterator()
123 verificationStatus := true
124 for (digsigFitr.HasNext()){
125 curr := digsigFitr.Current()
126 result := curr.Verify(opts)
127 if result.GetVerificationStatus(){
128 fmt.Printf("Signature verified, objnum: %d\n", curr.GetSDFObj().GetObjNum())
129 }else{
130 fmt.Printf("Signature verification failed, objnum: %d\n", curr.GetSDFObj().GetObjNum())
131 verificationStatus = false
132 }
133 digest_algorithm := result.GetDigestAlgorithm()
134 if digest_algorithm == DigestAlgorithmE_SHA1{
135 fmt.Println("Digest algorithm: SHA-1")
136 }else if digest_algorithm == DigestAlgorithmE_SHA256{
137 fmt.Println("Digest algorithm: SHA-256")
138 }else if digest_algorithm == DigestAlgorithmE_SHA384{
139 fmt.Println("Digest algorithm: SHA-384")
140 }else if digest_algorithm == DigestAlgorithmE_SHA512{
141 fmt.Println("Digest algorithm: SHA-512")
142 }else if digest_algorithm == DigestAlgorithmE_RIPEMD160{
143 fmt.Println("Digest algorithm: RIPEMD-160")
144 }else if digest_algorithm == DigestAlgorithmE_unknown_digest_algorithm{
145 fmt.Println("Digest algorithm: unknown")
146 }else{
147 //unrecognized document status
148 }
149 fmt.Printf("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s\n",
150 result.GetDocumentStatusAsString(),
151 result.GetDigestStatusAsString(),
152 result.GetTrustStatusAsString(),
153 result.GetPermissionsStatusAsString())
154
155 changes := result.GetDisallowedChanges()
156 for i := 0; i < int(changes.Size()); i++ {
157 fmt.Printf("\tDisallowed change: %s, objnum: %d\n", changes.Get(i).GetTypeAsString(), changes.Get(i).GetObjNum())
158 }
159
160 // Get and print all the detailed trust-related results, if they are available.
161 if result.HasTrustVerificationResult(){
162 trustVerificationResult := result.GetTrustVerificationResult()
163 var msg string
164 if trustVerificationResult.WasSuccessful(){
165 msg = "Trust verified."
166 } else {
167 msg = "Trust not verifiable."
168 }
169 fmt.Println(msg)
170 fmt.Println(trustVerificationResult.GetResultString())
171
172 tmpTimeTv := trustVerificationResult.GetTimeOfTrustVerification()
173
174 trustVerificationTimeEnum := trustVerificationResult.GetTimeOfTrustVerificationEnum()
175
176 if trustVerificationTimeEnum == VerificationOptionsE_current{
177 fmt.Println("Trust verification attempted with respect to current time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
178 }else if trustVerificationTimeEnum == VerificationOptionsE_signing{
179 fmt.Println("Trust verification attempted with respect to signing time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
180 }else if trustVerificationTimeEnum == VerificationOptionsE_timestamp{
181 fmt.Println("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
182 }else{
183 //unrecognized time enum value
184 }
185
186 if trustVerificationResult.GetCertPath().Size() == 0{
187 fmt.Println("Could not print certificate path.")
188 }else{
189 fmt.Println("Certificate path:")
190 certPath := trustVerificationResult.GetCertPath()
191 for i := 0; i < int(certPath.Size()); i ++{
192 fmt.Println("\tCertificate:")
193 fmt.Println("\t\tIssuer names:")
194 issuerDn := certPath.Get(i).GetIssuerField().GetAllAttributesAndValues()
195 for j := 0; j < int(issuerDn.Size()); j ++{
196 fmt.Println("\t\t\t" + issuerDn.Get(j).GetStringValue())
197 }
198 fmt.Println("\t\tSubject names:")
199 subjectDn := certPath.Get(i).GetSubjectField().GetAllAttributesAndValues()
200 for k := 0; k < int(subjectDn.Size()); k ++{
201 fmt.Println("\t\t\t" + subjectDn.Get(k).GetStringValue())
202 }
203 fmt.Println("\t\tExtensions:")
204 for m := 0; m < int(certPath.Get(i).GetExtensions().Size()); m ++{
205 fmt.Println("\t\t\t" + certPath.Get(i).GetExtensions().Get(m).ToString())
206 }
207 }
208 }
209 }else{
210 fmt.Println("No detailed trust verification result available.")
211
212 unsupportedFeatures := result.GetUnsupportedFeatures()
213 if unsupportedFeatures.Size() > 0 {
214 fmt.Println("Unsupported features:")
215 for i := 0; i < int(unsupportedFeatures.Size()); i ++{
216 fmt.Println("\t" + unsupportedFeatures.Get(i))
217 }
218 }
219 }
220 fmt.Println("==========")
221
222 digsigFitr.Next()
223 }
224 return verificationStatus
225}
226
227func CertifyPDF(inDocpath string,
228 inCertFieldName string ,
229 inPrivateKeyFilePath string,
230 inKeyfilePassword string,
231 inAppearanceImagePath string,
232 inOutpath string){
233
234 fmt.Println("================================================================================")
235 fmt.Println("Certifying PDF document")
236
237 // Open an existing PDF
238 doc := NewPDFDoc(inDocpath)
239
240 if doc.HasSignatures(){
241 fmt.Println("PDFDoc has signatures")
242 }else{
243 fmt.Println("PDFDoc has no signatures")
244 }
245 page1 := doc.GetPage(1)
246
247 // Create a text field that we can lock using the field permissions feature.
248 annot1 := TextWidgetCreate(doc, NewRect(143.0, 440.0, 350.0, 460.0), "asdf_test_field")
249 page1.AnnotPushBack(annot1)
250
251 // Create a new signature form field in the PDFDoc. The name argument is optional;
252 // leaving it empty causes it to be auto-generated. However, you may need the name for later.
253 // Acrobat doesn"t show digsigfield in side panel if it's without a widget. Using a
254 // Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
255 certificationSigField := doc.CreateDigitalSignatureField(inCertFieldName)
256 widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), certificationSigField)
257 page1.AnnotPushBack(widgetAnnot)
258
259 // (OPTIONAL) Add an appearance to the signature field.
260 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImagePath)
261 widgetAnnot.CreateSignatureAppearance(img)
262
263 // Add permissions. Lock the random text field.
264 fmt.Println("Adding document permissions.")
265 certificationSigField.SetDocumentPermissions(DigitalSignatureFieldE_annotating_formfilling_signing_allowed)
266
267 // Prepare to lock the text field that we created earlier.
268 fmt.Println("Adding field permissions.")
269 testField := NewVectorString()
270 testField.Add("asdf_test_field")
271 certificationSigField.SetFieldPermissions(DigitalSignatureFieldE_include, testField)
272
273 certificationSigField.CertifyOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)
274
275 // (OPTIONAL) Add more information to the signature dictionary.
276 certificationSigField.SetLocation("Vancouver, BC")
277 certificationSigField.SetReason("Document certification.")
278 certificationSigField.SetContactInfo("www.pdftron.com")
279
280 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
281 doc.Save(inOutpath, uint(0))
282
283 fmt.Println("================================================================================")
284}
285
286func SignPDF(inDocpath string,
287 inApprovalFieldName string,
288 inPrivateKeyFilePath string,
289 inKeyfilePassword string,
290 inAppearanceImgPath string,
291 inOutpath string){
292
293 fmt.Println("================================================================================")
294 fmt.Println("Signing PDF document")
295
296 // Open an existing PDF
297 doc := NewPDFDoc(inDocpath)
298
299 // Retrieve the unsigned approval signature field.
300 foundApprovalField := doc.GetField(inApprovalFieldName)
301 foundApprovalSignatureDigsigField := NewDigitalSignatureField(foundApprovalField)
302
303 // (OPTIONAL) Add an appearance to the signature field.
304 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
305 foundApprovalSignatureWidget := NewSignatureWidget(foundApprovalField.GetSDFObj())
306 foundApprovalSignatureWidget.CreateSignatureAppearance(img)
307
308 // Prepare the signature and signature handler for signing.
309 foundApprovalSignatureDigsigField.SignOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)
310
311 // The actual approval signing will be done during the following incremental save operation.
312 doc.Save(inOutpath, uint(SDFDocE_incremental))
313
314 fmt.Println("================================================================================")
315}
316
317func ClearSignature(inDocpath string,
318 inDigsigFieldName string,
319 inOutpath string){
320
321 fmt.Println("================================================================================")
322 fmt.Println("Clearing certification signature")
323
324 doc := NewPDFDoc(inDocpath)
325
326 digsig := NewDigitalSignatureField(doc.GetField(inDigsigFieldName))
327
328 fmt.Println("Clearing signature: " + inDigsigFieldName)
329 digsig.ClearSignature()
330
331 if !digsig.HasCryptographicSignature(){
332 fmt.Println("Cryptographic signature cleared properly.")
333 }
334 // Save incrementally so as to not invalidate other signatures from previous saves.
335 doc.Save(inOutpath, uint(SDFDocE_incremental))
336
337 fmt.Println("================================================================================")
338}
339
340func PrintSignaturesInfo(inDocpath string){
341 fmt.Println("================================================================================")
342 fmt.Println("Reading and printing digital signature information")
343
344 doc := NewPDFDoc(inDocpath)
345 if !doc.HasSignatures(){
346 fmt.Println("Doc has no signatures.")
347 fmt.Println("================================================================================")
348 return
349 }else{
350 fmt.Println("Doc has signatures.")
351 }
352
353 fitr := doc.GetFieldIterator()
354 for fitr.HasNext(){
355 current := fitr.Current()
356 if (current.IsLockedByDigitalSignature()){
357 fmt.Println("==========\nField locked by a digital signature")
358 }else{
359 fmt.Println("==========\nField not locked by a digital signature")
360 }
361 fmt.Println("Field name: " + current.GetName())
362 fmt.Println("==========")
363
364 fitr.Next()
365 }
366
367 fmt.Println("====================\nNow iterating over digital signatures only.\n====================")
368
369 digsigFitr := doc.GetDigitalSignatureFieldIterator()
370 for digsigFitr.HasNext(){
371 current := digsigFitr.Current()
372 fmt.Println("==========")
373 fmt.Println("Field name of digital signature: " + NewField(current.GetSDFObj()).GetName())
374
375 digsigfield := current
376 if !digsigfield.HasCryptographicSignature(){
377 fmt.Println("Either digital signature field lacks a digital signature dictionary, " +
378 "or digital signature dictionary lacks a cryptographic Contents entry. " +
379 "Digital signature field is not presently considered signed.\n" +
380 "==========")
381 digsigFitr.Next()
382 continue
383 }
384 certCount := digsigfield.GetCertCount()
385 fmt.Println("Cert count: " + strconv.Itoa(int(certCount)))
386 for i := uint(0); i < certCount; i ++{
387 cert := digsigfield.GetCert(i)
388 fmt.Println("Cert //" + strconv.Itoa(int(i)) + " size: " + strconv.Itoa(int(cert.Size())))
389 }
390 subfilter := digsigfield.GetSubFilter()
391
392 fmt.Println("Subfilter type: " + strconv.Itoa(int(subfilter)))
393
394 if subfilter != DigitalSignatureFieldE_ETSI_RFC3161{
395 fmt.Println("Signature's signer: " + digsigfield.GetSignatureName())
396
397 signingTime := digsigfield.GetSigningTime()
398 if signingTime.IsValid(){
399 fmt.Println("Signing time is valid.")
400 }
401 fmt.Println("Location: " + digsigfield.GetLocation())
402 fmt.Println("Reason: " + digsigfield.GetReason())
403 fmt.Println("Contact info: " + digsigfield.GetContactInfo())
404 }else{
405 fmt.Println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)")
406 }
407 if digsigfield.HasVisibleAppearance(){
408 fmt.Println("Visible")
409 }else{
410 fmt.Println("Not visible")
411 }
412 digsigDocPerms := digsigfield.GetDocumentPermissions()
413 lockedFields := digsigfield.GetLockedFields()
414 for i := 0; i < int(lockedFields.Size()); i ++{
415 fmt.Println("This digital signature locks a field named: " + lockedFields.Get(i))
416 }
417 if digsigDocPerms == DigitalSignatureFieldE_no_changes_allowed{
418 fmt.Println("No changes to the document can be made without invalidating this digital signature.")
419 }else if digsigDocPerms == DigitalSignatureFieldE_formfilling_signing_allowed{
420 fmt.Println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
421 }else if digsigDocPerms == DigitalSignatureFieldE_annotating_formfilling_signing_allowed{
422 fmt.Println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
423 }else if digsigDocPerms == DigitalSignatureFieldE_unrestricted{
424 fmt.Println("Document not restricted by this digital signature.")
425 }else{
426 fmt.Println("Unrecognized digital signature document permission level.")
427 }
428 fmt.Println("==========")
429 digsigFitr.Next()
430 }
431
432 fmt.Println("================================================================================")
433}
434
435func CustomSigningAPI(doc_path string,
436 cert_field_name string,
437 private_key_file_path string,
438 keyfile_password string,
439 public_key_file_path string,
440 appearance_image_path string,
441 digest_algorithm_type PdftronCryptoDigestAlgorithmType,
442 PAdES_signing_mode bool,
443 output_path string){
444 fmt.Println("================================================================================")
445 fmt.Println("Custom signing PDF document")
446
447 doc := NewPDFDoc(doc_path)
448
449 page1 := doc.GetPage(1)
450
451 digsig_field := doc.CreateDigitalSignatureField(cert_field_name)
452 widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), digsig_field)
453 page1.AnnotPushBack(widgetAnnot)
454
455 // (OPTIONAL) Add an appearance to the signature field.
456 img := ImageCreate(doc.GetSDFDoc(), appearance_image_path)
457 widgetAnnot.CreateSignatureAppearance(img)
458
459 signing_mode := DigitalSignatureFieldE_adbe_pkcs7_detached
460 if PAdES_signing_mode{
461 signing_mode = DigitalSignatureFieldE_ETSI_CAdES_detached
462 }
463 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
464 digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
465 &signing_mode,
466 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.
467 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
468
469 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
470 current_date := NewDate()
471 current_date.SetCurrentTime()
472 digsig_field.SetSigDictTimeOfSigning(current_date)
473
474 doc.Save(output_path, uint(SDFDocE_incremental))
475
476 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
477 pdf_digest := digsig_field.CalculateDigest(digest_algorithm_type)
478
479 signer_cert := NewX509Certificate(public_key_file_path)
480
481 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
482 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
483 pades_versioned_ess_signing_cert_attribute := DigitalSignatureFieldGenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)
484
485 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
486 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
487 signedAttrs := DigitalSignatureFieldGenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)
488
489 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
490 signedAttrs_digest := DigestAlgorithmCalculateDigest(digest_algorithm_type, signedAttrs)
491
492 ///////////////////////////// custom digest signing starts ////////////////////////////
493 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
494 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
495 signature_value := DigestAlgorithmSignDigest(
496 signedAttrs_digest,
497 digest_algorithm_type,
498 private_key_file_path,
499 keyfile_password)
500 ///////////////////////////// custom digest signing ends ///////////////////////////////
501
502 // Then, load all your chain certificates into a container of X509Certificate.
503 chain_certs := NewVectorX509Certificate()
504
505 // Then, create ObjectIdentifiers for the algorithms you have used.
506 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
507 digest_algorithm_oid := NewObjectIdentifier(digest_algorithm_type)
508 signature_algorithm_oid := NewObjectIdentifier(ObjectIdentifierE_RSA_encryption_PKCS1)
509
510 // Then, put the CMS signature components together.
511 cms_signature := DigitalSignatureFieldGenerateCMSSignature(
512 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
513 signature_value, signedAttrs)
514
515 // Write the signature to the document.
516 doc.SaveCustomSignature(cms_signature, digsig_field, output_path)
517
518 fmt.Println("================================================================================")
519}
520
521func TimestampAndEnableLTV(inDocpath string,
522 inTsaUrl string,
523 inTrustedCertPath string,
524 inAppearanceImgPath string,
525 inOutpath string) bool{
526 doc := NewPDFDoc(inDocpath)
527 doctimestampSignatureField := doc.CreateDigitalSignatureField()
528 tstConfig := NewTimestampingConfiguration(inTsaUrl)
529 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
530// It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
531// the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
532// response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
533// function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
534// in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.
535
536 opts.AddTrustedCertificate(inTrustedCertPath)
537// By default, we only check online for revocation of certificates using the newer and lighter
538// OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
539// it may be necessary to enable online CRL revocation checking in order to verify some timestamps
540// (i.e. those that do not have an OCSP responder URL for all non-trusted certificates).
541
542 opts.EnableOnlineCRLRevocationChecking(true)
543
544 widgetAnnot := SignatureWidgetCreate(doc, NewRect(0.0, 100.0, 200.0, 150.0), doctimestampSignatureField)
545 doc.GetPage(1).AnnotPushBack(widgetAnnot)
546
547 // (OPTIONAL) Add an appearance to the signature field.
548 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
549 widgetAnnot.CreateSignatureAppearance(img)
550
551 fmt.Println("Testing timestamping configuration.")
552 configResult := tstConfig.TestConfiguration(opts)
553 if configResult.GetStatus(){
554 fmt.Println("Success: timestamping configuration usable. Attempting to timestamp.")
555 }else{
556 // Print details of timestamping failure.
557 fmt.Println(configResult.GetString())
558 if configResult.HasResponseVerificationResult(){
559 tstResult := configResult.GetResponseVerificationResult()
560 fmt.Println("CMS digest status: "+ tstResult.GetCMSDigestStatusAsString())
561 fmt.Println("Message digest status: " + tstResult.GetMessageImprintDigestStatusAsString())
562 fmt.Println("Trust status: " + tstResult.GetTrustStatusAsString())
563 }
564 return false
565 }
566
567 doctimestampSignatureField.TimestampOnNextSave(tstConfig, opts)
568
569 // Save/signing throws if timestamping fails.
570 doc.Save(inOutpath, uint(SDFDocE_incremental))
571
572 fmt.Println("Timestamping successful. Adding LTV information for DocTimeStamp signature.")
573
574 // Add LTV information for timestamp signature to document.
575 timestampVerificationResult := doctimestampSignatureField.Verify(opts)
576 if !doctimestampSignatureField.EnableLTVOfflineVerification(timestampVerificationResult){
577 fmt.Println("Could not enable LTV for DocTimeStamp.")
578 return false
579 }
580 doc.Save(inOutpath, uint(SDFDocE_incremental))
581 fmt.Println("Added LTV information for DocTimeStamp signature successfully.")
582
583 return true
584}
585
586func TestDigitalSignatures(t *testing.T){
587 // Initialize PDFNet
588 PDFNetInitialize(licenseKey)
589
590 result := true
591 inputPath := "../TestFiles/"
592 outputPath := "../TestFiles/Output/"
593
594 //////////////////////////////////////// TEST 0:
595 // Create an approval signature field that we can sign after certifying.
596 // (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
597 // Open an existing PDF
598 doc := NewPDFDoc(inputPath + "waiver.pdf")
599 widgetAnnotApproval := SignatureWidgetCreate(doc, NewRect(300.0, 287.0, 376.0, 306.0), "PDFTronApprovalSig")
600 page1 := doc.GetPage(1)
601 page1.AnnotPushBack(widgetAnnotApproval)
602 doc.Save(outputPath + "waiver_withApprovalField_output.pdf", uint(SDFDocE_remove_unused))
603
604 //////////////////////////////////////// TEST 1: certify a PDF.
605 CertifyPDF(inputPath + "waiver_withApprovalField.pdf",
606 "PDFTronCertificationSig",
607 inputPath + "pdftron.pfx",
608 "password",
609 inputPath + "pdftron.bmp",
610 outputPath + "waiver_withApprovalField_certified_output.pdf")
611 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_output.pdf")
612
613 //////////////////////////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
614 SignPDF(inputPath + "waiver_withApprovalField_certified.pdf",
615 "PDFTronApprovalSig",
616 inputPath + "pdftron.pfx",
617 "password",
618 inputPath + "signature.jpg",
619 outputPath + "waiver_withApprovalField_certified_approved_output.pdf")
620 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_output.pdf")
621
622 //////////////////////////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
623 ClearSignature(inputPath + "waiver_withApprovalField_certified_approved.pdf",
624 "PDFTronCertificationSig",
625 outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
626 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
627
628 //////////////////////////////////////// TEST 4: Verify a document's digital signatures.
629 if !VerifyAllAndPrint(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
630 result = false
631 }
632
633 //////////////////////////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
634 if !VerifySimple(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
635 result = false
636 }
637
638 //////////////////// TEST 6: Custom signing API.
639 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
640 // which allows users to customize the process of signing documents. Among other things, this
641 // includes the capability to allow for easy integration of PDF-specific signing-related operations
642 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
643 // to system keystores, etc.
644 CustomSigningAPI(inputPath + "waiver.pdf",
645 "PDFTronApprovalSig",
646 inputPath + "pdftron.pfx",
647 "password",
648 inputPath + "pdftron.cer",
649 inputPath + "signature.jpg",
650 DigestAlgorithmE_SHA256,
651 true,
652 outputPath + "waiver_custom_signed.pdf")
653
654 //////////////////////////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
655
656 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
657 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
658 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
659 // tsaUrl := "YOUR_URL_OF_TSA"
660 //
661 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
662 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
663 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
664 // trustedCertPath := "YOUR_CERTIFICATE";
665 //
666 // if tsaUrl == "YOUR_URL_OF_TSA" {
667 // fmt.Println("Error: The URL of your timestamp authority was not specified.")
668 // result = false
669 // } else if trustedCertPath == "YOUR_CERTIFICATE" {
670 // fmt.Println("Error: The path to your timestamp authority trusted root certificate was not specified.")
671 // result = false
672 // } else if !TimestampAndEnableLTV(inputPath + "waiver.pdf",
673 // tsaUrl,
674 // trustedCertPath,
675 // inputPath + "signature.jpg",
676 // outputPath + "waiver_DocTimeStamp_LTV.pdf") {
677 // result = false
678 // }
679
680 //////////////////////////////////////// End of tests. ////////////////////////////////////////
681
682 if !result{
683 fmt.Println("Tests FAILED!!!\n==========")
684 PDFNetTerminate()
685 return
686 }
687 PDFNetTerminate()
688 fmt.Println("Tests successful.\n==========")
689}
1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6//----------------------------------------------------------------------------------------------------------------------
7// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
8//
9// The following steps reflect typical intended usage of the digital signatures API:
10//
11// 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)).
12//
13// 1. EITHER:
14// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
15// -OR-
16// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
17// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
18//
19// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
20// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
21// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
22//
23// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
24// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
25// certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
26// is signed.]
27//
28// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
29// a. Taking a PKCS #12 keyfile path and its password
30// b. Taking a buffer containing a PKCS #12 private keyfile and its password
31// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
32// in the following fashion:
33// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
34// validate/check a digital signature.
35// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
36// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
37// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
38// NOTE: It is only possible to sign/certify one signature per call to the Save function.
39//
40// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
41// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
42// so as to not invalidate the other signature(s).
43//
44// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
45// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
46// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
47//
48// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
49// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
50//----------------------------------------------------------------------------------------------------------------------
51
52// To build and run this sample with OpenSSL, please specify OpenSSL include & lib paths to project settings.
53//
54// In MSVC, this can be done by opening the DigitalSignatureTest project's properties. Go to Configuration Properties ->
55// C/C++ -> General -> Additional Include Directories. Add the path to the OpenSSL headers here. Next, go to
56// Configuration Properties -> Linker -> General -> Additional Library Directories. Add the path to the OpenSSL libraries
57// here. Finally, under Configuration Properties -> Linker -> Input -> Additional Dependencies, add libeay32.lib,
58// crypt32.lib, and advapi32.lib in the list.
59//
60// For GCC, modify the Makefile, add -lcrypto to the $(LIBS) variable. If OpenSSL is installed elsewhere, it may be
61// necessary to add the path to the headers in the $(INCLUDE) variable as well as the location of either libcrypto.a or
62// libcrypto.so/libcrypto.dylib.
63//
64
65#define USE_STD_SIGNATURE_HANDLER 1 // Comment out this line if you intend to use OpenSSLSignatureHandler rather than StdSignatureHandler.
66
67// standard library includes
68#include <cstdio>
69#include <iostream>
70#include <vector>
71
72// PDFNetC includes
73#include <Common/Exception.h>
74#include <Common/UString.h>
75#include <PDF/Page.h>
76#include <PDF/Annot.h>
77#include <PDF/Annots/TextWidget.h>
78#include <PDF/Date.h>
79#include <PDF/Element.h>
80#include <PDF/ElementBuilder.h>
81#include <PDF/ElementWriter.h>
82#include <PDF/Field.h>
83#include <PDF/Image.h>
84#include <PDF/PDFDoc.h>
85#include <PDF/PDFNet.h>
86#include <SDF/SignatureHandler.h>
87#include <PDF/Annots/SignatureWidget.h>
88#include <PDF/VerificationResult.h>
89#include <PDF/TrustVerificationResult.h>
90#include <PDF/DisallowedChange.h>
91#include <Filters/MappedFile.h>
92#include <Crypto/X501AttributeTypeAndValue.h>
93#include "../../LicenseKey/CPP/LicenseKey.h"
94
95#if (!USE_STD_SIGNATURE_HANDLER)
96// OpenSSL includes
97#include <openssl/err.h>
98#include <openssl/evp.h>
99#include <openssl/pkcs12.h>
100#include <openssl/pkcs7.h>
101#include <openssl/rsa.h>
102#include <openssl/sha.h>
103#endif // (!USE_STD_SIGNATURE_HANDLER)
104
105using namespace std;
106using namespace pdftron;
107using namespace pdftron::SDF;
108using namespace pdftron::PDF::Annots;
109using namespace pdftron::PDF;
110
111//////////////////// Here follows an example of how to implement a custom signature handler. //////////
112#if (!USE_STD_SIGNATURE_HANDLER)
113//
114// Extend SignatureHandler by using OpenSSL signing utilities.
115//
116class OpenSSLSignatureHandler : public SignatureHandler
117{
118public:
119 OpenSSLSignatureHandler(const char* in_pfxfile, const char* in_password) : m_pfxfile(in_pfxfile), m_password(in_password)
120 {
121 // Please note: this code would require changes to support non-US-ASCII paths on platforms where UTF-8 is not usable within fopen (e.g. Windows).
122 FILE* fp = fopen(in_pfxfile, "rb");
123 if (fp == NULL)
124 throw (Common::Exception("Cannot open private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot open private key."));
125
126 PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
127 fclose(fp);
128
129 if (p12 == NULL)
130 throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));
131
132 mp_pkey = NULL;
133 mp_x509 = NULL;
134 mp_ca = NULL;
135 int parseResult = PKCS12_parse(p12, in_password, &mp_pkey, &mp_x509, &mp_ca);
136 PKCS12_free(p12);
137
138 if (parseResult == 0)
139 throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));
140
141 Reset();
142 }
143
144 virtual UString GetName() const
145 {
146 return "Adobe.PPKLite";
147 }
148
149 virtual void AppendData(const std::vector<pdftron::UInt8>& in_data)
150 {
151 SHA256_Update(&m_sha_ctx, (const void*) &(in_data[0]), in_data.size());
152 return;
153 }
154
155 virtual bool Reset()
156 {
157 m_digest.resize(0);
158 m_digest.clear();
159 SHA256_Init(&m_sha_ctx);
160 return (true);
161 }
162
163 virtual std::vector<pdftron::UInt8> CreateSignature()
164 {
165 if (m_digest.size() == 0) {
166 m_digest.resize(SHA256_DIGEST_LENGTH);
167 SHA256_Final(&(m_digest[0]), &m_sha_ctx);
168 }
169
170 PKCS7* p7 = PKCS7_new();
171 PKCS7_set_type(p7, NID_pkcs7_signed);
172
173 PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha256());
174 PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
175 PKCS7_add0_attrib_signing_time(p7Si, NULL);
176 PKCS7_add1_attrib_digest(p7Si, &(m_digest[0]), (int)m_digest.size());
177 PKCS7_add_certificate(p7, mp_x509);
178
179 for (int c = 0; c < sk_X509_num(mp_ca); c++) {
180 X509* cert = sk_X509_value(mp_ca, c);
181 PKCS7_add_certificate(p7, cert);
182 }
183 PKCS7_set_detached(p7, 1);
184 PKCS7_content_new(p7, NID_pkcs7_data);
185
186 PKCS7_SIGNER_INFO_sign(p7Si);
187
188 int p7Len = i2d_PKCS7(p7, NULL);
189 std::vector<unsigned char> result(p7Len);
190 UInt8* pP7Buf = &(result[0]);
191 i2d_PKCS7(p7, &pP7Buf);
192
193 PKCS7_free(p7);
194
195 return (result);
196 }
197
198 virtual OpenSSLSignatureHandler* Clone() const
199 {
200 return (new OpenSSLSignatureHandler(m_pfxfile.c_str(), m_password.c_str()));
201 }
202
203 virtual ~OpenSSLSignatureHandler()
204 {
205 sk_X509_free(mp_ca);
206 X509_free(mp_x509);
207 EVP_PKEY_free(mp_pkey);
208 }
209
210private:
211 std::vector<UInt8> m_digest;
212 std::string m_pfxfile;
213 std::string m_password;
214
215 SHA256_CTX m_sha_ctx;
216 EVP_PKEY* mp_pkey; // private key
217 X509* mp_x509; // signing certificate
218 STACK_OF(X509)* mp_ca; // certificate chain up to the CA
219}; // class OpenSSLSignatureHandler
220#endif // (!USE_STD_SIGNATURE_HANDLER)
221////////// End of the OpenSSLSignatureHandler custom handler code. ////////////////////
222
223
224string input_path = "../../TestFiles/";
225string output_path = "../../TestFiles/Output/";
226
227bool VerifySimple(const UString& in_docpath, const UString& in_public_key_file_path)
228{
229 PDFDoc doc(in_docpath);
230 puts("==========");
231 VerificationOptions opts(VerificationOptions::e_compatibility_and_archiving);
232
233 // Add trust root to store of trusted certificates contained in VerificationOptions.
234 opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);
235
236 enum PDFDoc::SignaturesVerificationStatus result = doc.VerifySignedDigitalSignatures(opts);
237 switch (result)
238 {
239 case PDFDoc::e_unsigned:
240 puts("Document has no signed signature fields.");
241 return false;
242 /* e_failure == bad doc status, digest status, or permissions status
243 (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
244 case PDFDoc::e_failure:
245 puts("Hard failure in verification on at least one signature.");
246 return false;
247 case PDFDoc::e_untrusted:
248 puts("Could not verify trust for at least one signature.");
249 return false;
250 case PDFDoc::e_unsupported:
251 /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
252 unsupported features were encountered (requires verification using 'detailed' APIs) */
253 puts("At least one signature contains unsupported features.");
254 return false;
255 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
256 case PDFDoc::e_verified:
257 puts("All signed signatures in document verified.");
258 return true;
259 default:
260 BASE_ASSERT(false, "unrecognized document verification status");
261 }
262}
263
264bool VerifyAllAndPrint(const UString& in_docpath, const UString& in_public_key_file_path)
265{
266 PDFDoc doc(in_docpath);
267 cout << "==========" << endl;
268 PDF::VerificationOptions opts(PDF::VerificationOptions::e_compatibility_and_archiving);
269
270 /* Add trust root to store of trusted certificates contained in VerificationOptions.
271 Use trust level corresponding to an identity trusted even for certification signatures. */
272 opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);
273
274 // Iterate over the signatures and verify all of them.
275 DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
276 bool verification_status = true;
277 for (; digsig_fitr.HasNext(); digsig_fitr.Next())
278 {
279 DigitalSignatureField curr = digsig_fitr.Current();
280 VerificationResult result = curr.Verify(opts);
281 if (result.GetVerificationStatus())
282 {
283 cout << "Signature verified, ";
284 }
285 else
286 {
287 cout << "Signature verification failed, ";
288 verification_status = false;
289 }
290 cout << "objnum: " << curr.GetSDFObj().GetObjNum() << endl;
291
292 switch (result.GetDigestAlgorithm())
293 {
294 case Crypto::DigestAlgorithm::e_SHA1:
295 cout << "Digest algorithm: SHA-1" << endl;
296 break;
297 case Crypto::DigestAlgorithm::e_SHA256:
298 cout << "Digest algorithm: SHA-256" << endl;
299 break;
300 case Crypto::DigestAlgorithm::e_SHA384:
301 cout << "Digest algorithm: SHA-384" << endl;
302 break;
303 case Crypto::DigestAlgorithm::e_SHA512:
304 cout << "Digest algorithm: SHA-512" << endl;
305 break;
306 case Crypto::DigestAlgorithm::e_RIPEMD160:
307 cout << "Digest algorithm: RIPEMD-160" << endl;
308 break;
309 case Crypto::DigestAlgorithm::e_unknown_digest_algorithm:
310 cout << "Digest algorithm: unknown" << endl;
311 break;
312 default:
313 BASE_ASSERT(false, "unrecognized digest algorithm");
314 }
315 printf("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s\n",
316 result.GetDocumentStatusAsString().ConvertToUtf8().c_str(),
317 result.GetDigestStatusAsString().ConvertToUtf8().c_str(),
318 result.GetTrustStatusAsString().ConvertToUtf8().c_str(),
319 result.GetPermissionsStatusAsString().ConvertToUtf8().c_str());
320
321
322 std::vector<DisallowedChange> changes = result.GetDisallowedChanges();
323 for (std::vector<DisallowedChange>::iterator it2 = changes.begin(); it2 != changes.end(); ++it2)
324 {
325 cout << "\tDisallowed change: " << it2->GetTypeAsString().ConvertToUtf8() << ", objnum: " << it2->GetObjNum() << endl;
326 }
327
328 // Get and print all the detailed trust-related results, if they are available.
329 if (result.HasTrustVerificationResult())
330 {
331 const TrustVerificationResult trust_verification_result = result.GetTrustVerificationResult();
332 trust_verification_result.WasSuccessful()? cout << "Trust verified." << endl : cout << "Trust not verifiable." << endl;
333 cout << trust_verification_result.GetResultString().ConvertToUtf8() << endl;
334
335 Int64 tmp_time_t = trust_verification_result.GetTimeOfTrustVerification();
336 switch (trust_verification_result.GetTimeOfTrustVerificationEnum())
337 {
338 case VerificationOptions::e_current:
339 cout << "Trust verification attempted with respect to current time (as epoch time): " << tmp_time_t << endl;
340 break;
341 case VerificationOptions::e_signing:
342 cout << "Trust verification attempted with respect to signing time (as epoch time): " << tmp_time_t << endl;
343 break;
344 case VerificationOptions::e_timestamp:
345 cout << "Trust verification attempted with respect to secure embedded timestamp (as epoch time): " << tmp_time_t << endl;
346 break;
347 default:
348 BASE_ASSERT(false, "unrecognized time enum value");
349 }
350
351 if (trust_verification_result.GetCertPath().empty())
352 {
353 cout << "Could not print certificate path.\n";
354 }
355 else
356 {
357 cout << "Certificate path:\n";
358 std::vector<Crypto::X509Certificate> cert_path(trust_verification_result.GetCertPath());
359 for (std::vector<Crypto::X509Certificate>::iterator cert_path_begin = cert_path.begin();
360 cert_path_begin != cert_path.end(); ++cert_path_begin)
361 {
362 cout << "\tCertificate:\n";
363 Crypto::X509Certificate& full_cert(*cert_path_begin);
364 cout << "\t\tIssuer names:\n";
365 std::vector<Crypto::X501AttributeTypeAndValue> issuer_dn (full_cert.GetIssuerField().GetAllAttributesAndValues());
366 for (size_t i = 0; i < issuer_dn.size(); i++)
367 {
368 cout << "\t\t\t" << issuer_dn[i].GetStringValue().ConvertToUtf8() << "\n";
369 }
370 cout << "\t\tSubject names:\n";
371 std::vector<Crypto::X501AttributeTypeAndValue > subject_dn(full_cert.GetSubjectField().GetAllAttributesAndValues());
372 for (size_t i = 0; i < subject_dn.size(); i++)
373 {
374 cout << "\t\t\t" << subject_dn[i].GetStringValue().ConvertToUtf8() << "\n";
375 }
376 cout << "\t\tExtensions:\n";
377 for (size_t i = 0; i < full_cert.GetExtensions().size(); i++)
378 {
379 cout << "\t\t\t" << full_cert.GetExtensions()[i].ToString() << "\n";
380 }
381 }
382 }
383 }
384 else
385 {
386 cout << "No detailed trust verification result available." << endl;
387 }
388
389 const std::vector<UString> unsupported_features(result.GetUnsupportedFeatures());
390 if (unsupported_features.size() > 0)
391 {
392 cout << "Unsupported features:" << "\n";
393 for (size_t i = 0; i < unsupported_features.size(); i++)
394 {
395 cout << "\t" << unsupported_features[i].ConvertToUtf8() << "\n";
396 }
397 }
398 cout << "==========" << endl;
399 }
400
401 return verification_status;
402}
403
404void CertifyPDF(const UString& in_docpath,
405 const UString& in_cert_field_name,
406 const UString& in_private_key_file_path,
407 const UString& in_keyfile_password,
408 const UString& in_appearance_image_path,
409 const UString& in_outpath)
410{
411 cout << "================================================================================" << endl;
412 cout << "Certifying PDF document" << endl;
413
414 // Open an existing PDF
415 PDFDoc doc(in_docpath);
416
417 cout << "PDFDoc has " << (doc.HasSignatures() ? "signatures" : "no signatures") << endl;
418
419 Page page1 = doc.GetPage(1);
420
421 // Create a text field that we can lock using the field permissions feature.
422 Annots::TextWidget annot1 = Annots::TextWidget::Create(doc, Rect(143, 440, 350, 460), "asdf_test_field");
423 page1.AnnotPushBack(annot1);
424
425 /* Create a new signature form field in the PDFDoc. The name argument is optional;
426 leaving it empty causes it to be auto-generated. However, you may need the name for later.
427 Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
428 Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
429 PDF::DigitalSignatureField certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name);
430 Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(143, 287, 219, 306), certification_sig_field);
431 page1.AnnotPushBack(widgetAnnot);
432
433 // (OPTIONAL) Add an appearance to the signature field.
434 PDF::Image img = PDF::Image::Create(doc, in_appearance_image_path);
435 widgetAnnot.CreateSignatureAppearance(img);
436
437 // Prepare the document locking permission level. It will be applied upon document certification.
438 cout << "Adding document permissions." << endl;
439 certification_sig_field.SetDocumentPermissions(DigitalSignatureField::e_annotating_formfilling_signing_allowed);
440
441 // Prepare to lock the text field that we created earlier.
442 cout << "Adding field permissions." << endl;
443 vector<UString> fields_to_lock;
444 fields_to_lock.push_back("asdf_test_field");
445 certification_sig_field.SetFieldPermissions(DigitalSignatureField::e_include, fields_to_lock);
446
447#ifdef USE_STD_SIGNATURE_HANDLER
448 certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password);
449#else
450 OpenSSLSignatureHandler sigHandler(in_private_key_file_path.ConvertToUtf8().c_str(), in_keyfile_password.ConvertToUtf8().c_str());
451 SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
452 certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId);
453 /* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format
454 for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format,
455 so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
456 when using the StdSignatureHandler. */
457 Obj f_obj = certification_sig_field.GetSDFObj();
458 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
459#endif
460
461 // (OPTIONAL) Add more information to the signature dictionary.
462 certification_sig_field.SetLocation("Vancouver, BC");
463 certification_sig_field.SetReason("Document certification.");
464 certification_sig_field.SetContactInfo("www.pdftron.com");
465
466 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
467 doc.Save(in_outpath, 0, NULL);
468
469 cout << "================================================================================" << endl;
470}
471
472void SignPDF(const UString& in_docpath,
473 const UString& in_approval_field_name,
474 const UString& in_private_key_file_path,
475 const UString& in_keyfile_password,
476 const UString& in_appearance_img_path,
477 const UString& in_outpath)
478{
479 cout << "================================================================================" << endl;
480 cout << "Signing PDF document" << endl;
481
482 // Open an existing PDF
483 PDFDoc doc(in_docpath);
484
485 // Retrieve the unsigned approval signature field.
486 Field found_approval_field(doc.GetField(in_approval_field_name));
487 PDF::DigitalSignatureField found_approval_signature_digsig_field(found_approval_field);
488
489 // (OPTIONAL) Add an appearance to the signature field.
490 PDF::Image img = PDF::Image::Create(doc, in_appearance_img_path);
491 Annots::SignatureWidget found_approval_signature_widget(found_approval_field.GetSDFObj());
492 found_approval_signature_widget.CreateSignatureAppearance(img);
493
494 // Prepare the signature and signature handler for signing.
495#ifdef USE_STD_SIGNATURE_HANDLER
496 found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password);
497#else
498 OpenSSLSignatureHandler sigHandler(in_private_key_file_path.ConvertToUtf8().c_str(), in_keyfile_password.ConvertToUtf8().c_str());
499 SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
500 found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId);
501 /* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format
502 for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format,
503 so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
504 when using the StdSignatureHandler. */
505 Obj f_obj = found_approval_signature_digsig_field.GetSDFObj();
506 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
507#endif
508
509 // The actual approval signing will be done during the following incremental save operation.
510 doc.Save(in_outpath, SDFDoc::e_incremental, NULL);
511
512 cout << "================================================================================" << endl;
513}
514
515void ClearSignature(const UString& in_docpath,
516 const UString& in_digsig_field_name,
517 const UString& in_outpath)
518{
519 cout << "================================================================================" << endl;
520 cout << "Clearing certification signature" << endl;
521
522 PDFDoc doc(in_docpath);
523
524 DigitalSignatureField digsig(doc.GetField(in_digsig_field_name));
525
526 cout << "Clearing signature: " << in_digsig_field_name << endl;
527 digsig.ClearSignature();
528
529 if (!digsig.HasCryptographicSignature())
530 {
531 cout << "Cryptographic signature cleared properly." << endl;
532 }
533
534 // Save incrementally so as to not invalidate other signatures from previous saves.
535 doc.Save(in_outpath, SDFDoc::e_incremental, NULL);
536
537 cout << "================================================================================" << endl;
538}
539
540void PrintSignaturesInfo(const UString& in_docpath)
541{
542 cout << "================================================================================" << endl;
543 cout << "Reading and printing digital signature information" << endl;
544
545 PDFDoc doc(in_docpath);
546 if (!doc.HasSignatures())
547 {
548 cout << "Doc has no signatures." << endl;
549 cout << "================================================================================" << endl;
550 return;
551 }
552 else
553 {
554 cout << "Doc has signatures." << endl;
555 }
556
557
558 for (FieldIterator fitr = doc.GetFieldIterator(); fitr.HasNext(); fitr.Next())
559 {
560 fitr.Current().IsLockedByDigitalSignature() ? cout << "==========" << endl << "Field locked by a digital signature" << endl :
561 cout << "==========" << endl << "Field not locked by a digital signature" << endl;
562
563 cout << "Field name: " << fitr.Current().GetName() << endl;
564 cout << "==========" << endl;
565 }
566
567 cout << "====================" << endl << "Now iterating over digital signatures only." << endl << "====================" << endl;
568
569 DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
570 for (; digsig_fitr.HasNext(); digsig_fitr.Next())
571 {
572 cout << "==========" << endl;
573 cout << "Field name of digital signature: " << Field(digsig_fitr.Current().GetSDFObj()).GetName() << endl;
574
575 DigitalSignatureField digsigfield(digsig_fitr.Current());
576 if (!digsigfield.HasCryptographicSignature())
577 {
578 cout << "Either digital signature field lacks a digital signature dictionary, "
579 "or digital signature dictionary lacks a cryptographic Contents entry. "
580 "Digital signature field is not presently considered signed." << endl
581 << "==========" << endl;
582 continue;
583 }
584
585 UInt32 cert_count = digsigfield.GetCertCount();
586 cout << "Cert count: " << cert_count << endl;
587 for (UInt32 i = 0; i < cert_count; ++i)
588 {
589 std::vector<unsigned char> cert = digsigfield.GetCert(i);
590 cout << "Cert #" << i << " size: " << cert.size() << endl;
591 }
592
593 DigitalSignatureField::SubFilterType subfilter = digsigfield.GetSubFilter();
594
595 cout << "Subfilter type: " << (int)subfilter << endl;
596
597 if (subfilter != DigitalSignatureField::e_ETSI_RFC3161)
598 {
599 cout << "Signature's signer: " << digsigfield.GetSignatureName() << endl;
600
601 Date signing_time(digsigfield.GetSigningTime());
602 if (signing_time.IsValid())
603 {
604 cout << "Signing time is valid." << endl;
605 }
606
607 cout << "Location: " << digsigfield.GetLocation() << endl;
608 cout << "Reason: " << digsigfield.GetReason() << endl;
609 cout << "Contact info: " << digsigfield.GetContactInfo() << endl;
610 }
611 else
612 {
613 cout << "SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)" << endl;
614 }
615
616 cout << ((digsigfield.HasVisibleAppearance()) ? "Visible" : "Not visible") << endl;
617
618 DigitalSignatureField::DocumentPermissions digsig_doc_perms = digsigfield.GetDocumentPermissions();
619 vector<UString> locked_fields(digsigfield.GetLockedFields());
620 for (vector<UString>::iterator it = locked_fields.begin(); it != locked_fields.end(); ++it)
621 {
622 cout << "This digital signature locks a field named: " << it->ConvertToAscii() << endl;
623 }
624
625 switch (digsig_doc_perms)
626 {
627 case DigitalSignatureField::e_no_changes_allowed:
628 cout << "No changes to the document can be made without invalidating this digital signature." << endl;
629 break;
630 case DigitalSignatureField::e_formfilling_signing_allowed:
631 cout << "Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature." << endl;
632 break;
633 case DigitalSignatureField::e_annotating_formfilling_signing_allowed:
634 cout << "Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature." << endl;
635 break;
636 case DigitalSignatureField::e_unrestricted:
637 cout << "Document not restricted by this digital signature." << endl;
638 break;
639 default:
640 BASE_ASSERT(false, "Unrecognized digital signature document permission level.");
641 }
642 cout << "==========" << endl;
643 }
644
645 cout << "================================================================================" << endl;
646}
647
648void CustomSigningAPI(const UString& doc_path,
649 const UString& cert_field_name,
650 const UString& private_key_file_path,
651 const UString& keyfile_password,
652 const UString& public_key_file_path,
653 const UString& appearance_image_path,
654 const Crypto::DigestAlgorithm::Type digest_algorithm_type,
655 const bool PAdES_signing_mode,
656 const UString& output_path)
657{
658 cout << "================================================================================" << endl;
659 cout << "Custom signing PDF document" << endl;
660
661 PDFDoc doc(doc_path);
662
663 Page page1 = doc.GetPage(1);
664
665 DigitalSignatureField digsig_field = doc.CreateDigitalSignatureField(cert_field_name);
666 Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(143, 287, 219, 306), digsig_field);
667 page1.AnnotPushBack(widgetAnnot);
668
669 // (OPTIONAL) Add an appearance to the signature field.
670 PDF::Image img = PDF::Image::Create(doc, appearance_image_path);
671 widgetAnnot.CreateSignatureAppearance(img);
672
673 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
674 digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
675 PAdES_signing_mode ? DigitalSignatureField::e_ETSI_CAdES_detached : DigitalSignatureField::e_adbe_pkcs7_detached,
676 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.
677 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
678
679 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
680 Date current_date;
681 current_date.SetCurrentTime();
682 digsig_field.SetSigDictTimeOfSigning(current_date);
683
684 doc.Save(output_path, SDFDoc::e_incremental, NULL);
685
686 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
687 std::vector<UChar> pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type);
688
689 Crypto::X509Certificate signer_cert(public_key_file_path);
690
691 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
692 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
693 std::vector<UChar> pades_versioned_ess_signing_cert_attribute = DigitalSignatureField::GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);
694
695 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
696 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
697 std::vector<UChar> signedAttrs = DigitalSignatureField::GenerateCMSSignedAttributes(pdf_digest.data(), pdf_digest.size(),
698 pades_versioned_ess_signing_cert_attribute.data(), pades_versioned_ess_signing_cert_attribute.size());
699
700 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
701 std::vector<UChar> signedAttrs_digest = Crypto::DigestAlgorithm::CalculateDigest(digest_algorithm_type, signedAttrs.data(), signedAttrs.size());
702
703 //////////////////////////// custom digest signing starts ////////////////////////////
704 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
705 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
706 std::vector<UChar> signature_value = Crypto::DigestAlgorithm::SignDigest(
707 signedAttrs_digest.data(),
708 signedAttrs_digest.size(),
709 digest_algorithm_type,
710 private_key_file_path,
711 keyfile_password);
712 //////////////////////////// custom digest signing ends //////////////////////////////
713
714 // Then, load all your chain certificates into a container of X509Certificate.
715 std::vector<Crypto::X509Certificate> chain_certs;
716
717 // Then, create ObjectIdentifiers for the algorithms you have used.
718 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
719 Crypto::ObjectIdentifier digest_algorithm_oid(digest_algorithm_type);
720 Crypto::ObjectIdentifier signature_algorithm_oid(Crypto::ObjectIdentifier::e_RSA_encryption_PKCS1);
721
722 // Then, put the CMS signature components together.
723 std::vector<UChar> cms_signature = DigitalSignatureField::GenerateCMSSignature(
724 signer_cert, chain_certs.data(), chain_certs.size(), digest_algorithm_oid, signature_algorithm_oid,
725 signature_value.data(), signature_value.size(), signedAttrs.data(), signedAttrs.size());
726
727 // Write the signature to the document.
728 doc.SaveCustomSignature(cms_signature.data(), cms_signature.size(), digsig_field, output_path);
729
730 cout << "================================================================================" << endl;
731}
732
733bool TimestampAndEnableLTV(const UString& doc_path,
734 const UString& tsa_url,
735 const UString& trusted_cert_path,
736 const UString& appearance_img_path,
737 const UString& output_path)
738{
739 PDFDoc doc(doc_path);
740 DigitalSignatureField doctimestamp_signature_field = doc.CreateDigitalSignatureField();
741 TimestampingConfiguration tst_config(tsa_url);
742 VerificationOptions opts(VerificationOptions::e_compatibility_and_archiving);
743 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
744 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
745 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
746 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
747 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
748 opts.AddTrustedCertificate(trusted_cert_path);
749 /* By default, we only check online for revocation of certificates using the newer and lighter
750 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
751 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
752 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
753 opts.EnableOnlineCRLRevocationChecking(true);
754
755 Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(0, 0, 0, 0), doctimestamp_signature_field);
756 doc.GetPage(1).AnnotPushBack(widgetAnnot);
757
758 puts("Testing timestamping configuration.");
759 const TimestampingResult config_result(tst_config.TestConfiguration(opts));
760 if (config_result.GetStatus())
761 {
762 puts("Success: timestamping configuration usable. Attempting to timestamp.");
763 }
764 else
765 {
766 // Print details of timestamping failure.
767 puts(config_result.GetString().ConvertToUtf8().c_str());
768 if (config_result.HasResponseVerificationResult())
769 {
770 EmbeddedTimestampVerificationResult tst_result(config_result.GetResponseVerificationResult());
771 printf("CMS digest status: %s\n", tst_result.GetCMSDigestStatusAsString().ConvertToUtf8().c_str());
772 printf("Message digest status: %s\n", tst_result.GetMessageImprintDigestStatusAsString().ConvertToUtf8().c_str());
773 printf("Trust status: %s\n", tst_result.GetTrustStatusAsString().ConvertToUtf8().c_str());
774 }
775 return false;
776 }
777
778 doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts);
779
780 // Save/signing throws if timestamping fails.
781 doc.Save(output_path, SDFDoc::e_incremental, 0);
782
783 puts("Timestamping successful. Adding LTV information for DocTimeStamp signature.");
784
785 // Add LTV information for timestamp signature to document.
786 VerificationResult timestamp_verification_result = doctimestamp_signature_field.Verify(opts);
787 if (!doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result))
788 {
789 puts("Could not enable LTV for DocTimeStamp.");
790 return false;
791 }
792 doc.Save(output_path, SDFDoc::e_incremental, 0);
793 puts("Added LTV information for DocTimeStamp signature successfully.");
794
795 return true;
796}
797int main(void)
798{
799 // Initialize PDFNetC
800 PDFNet::Initialize(LicenseKey);
801
802#if (!USE_STD_SIGNATURE_HANDLER)
803 // Initialize OpenSSL library
804 CRYPTO_malloc_init();
805 ERR_load_crypto_strings();
806 OpenSSL_add_all_algorithms();
807#endif // (!USE_STD_SIGNATURE_HANDLER)
808
809 int ret = 0;
810
811 //////////////////// TEST 0:
812 /* Create an approval signature field that we can sign after certifying.
813 (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
814 try
815 {
816 PDFDoc doc(input_path + "waiver.pdf");
817 DigitalSignatureField approval_signature_field = doc.CreateDigitalSignatureField("PDFTronApprovalSig");
818 Annots::SignatureWidget widgetAnnotApproval = Annots::SignatureWidget::Create(doc, Rect(300, 287, 376, 306), approval_signature_field);
819 Page page1 = doc.GetPage(1);
820 page1.AnnotPushBack(widgetAnnotApproval);
821 doc.Save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc::e_remove_unused, 0);
822 }
823 catch (Common::Exception& e)
824 {
825 cerr << e << endl;
826 ret = 1;
827 }
828 catch (exception& e)
829 {
830 cerr << e.what() << endl;
831 ret = 1;
832 }
833 catch (...)
834 {
835 cerr << "Unknown exception." << endl;
836 ret = 1;
837 }
838
839 //////////////////// TEST 1: certify a PDF.
840 try
841 {
842 CertifyPDF(input_path + "waiver_withApprovalField.pdf",
843 "PDFTronCertificationSig",
844 input_path + "pdftron.pfx",
845 "password",
846 input_path + "pdftron.bmp",
847 output_path + "waiver_withApprovalField_certified_output.pdf");
848 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
849 }
850 catch (Common::Exception& e)
851 {
852 cerr << e << endl;
853 ret = 1;
854 }
855 catch (exception& e)
856 {
857 cerr << e.what() << endl;
858 ret = 1;
859 }
860 catch (...)
861 {
862 cerr << "Unknown exception." << endl;
863 ret = 1;
864 }
865
866 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
867 try
868 {
869 SignPDF(input_path + "waiver_withApprovalField_certified.pdf",
870 "PDFTronApprovalSig",
871 input_path + "pdftron.pfx",
872 "password",
873 input_path + "signature.jpg",
874 output_path + "waiver_withApprovalField_certified_approved_output.pdf");
875 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
876 }
877 catch (Common::Exception& e)
878 {
879 cerr << e << endl;
880 ret = 1;
881 }
882 catch (exception& e)
883 {
884 cerr << e.what() << endl;
885 ret = 1;
886 }
887 catch (...)
888 {
889 cerr << "Unknown exception." << endl;
890 ret = 1;
891 }
892
893 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
894 try
895 {
896 ClearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
897 "PDFTronCertificationSig",
898 output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
899 PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
900 }
901 catch (Common::Exception& e)
902 {
903 cerr << e << endl;
904 ret = 1;
905 }
906 catch (exception& e)
907 {
908 cerr << e.what() << endl;
909 ret = 1;
910 }
911 catch (...)
912 {
913 cerr << "Unknown exception." << endl;
914 ret = 1;
915 }
916
917 //////////////////// TEST 4: Verify a document's digital signatures.
918 try
919 {
920 if (!VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"))
921 {
922 ret = 1;
923 }
924 }
925 catch (Common::Exception& e)
926 {
927 cerr << e << endl;
928 ret = 1;
929 }
930 catch (exception& e)
931 {
932 cerr << e.what() << endl;
933 ret = 1;
934 }
935 catch (...)
936 {
937 cerr << "Unknown exception." << endl;
938 ret = 1;
939 }
940
941 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
942 try
943 {
944 if (!VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"))
945 {
946 ret = 1;
947 }
948 }
949 catch (Common::Exception& e)
950 {
951 cerr << e << endl;
952 ret = 1;
953 }
954 catch (exception& e)
955 {
956 cerr << e.what() << "\n";
957 ret = 1;
958 }
959 catch (...)
960 {
961 cerr << "Unknown exception.\n";
962 ret = 1;
963 }
964
965 //////////////////// TEST 6: Custom signing API.
966 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
967 // which allows users to customize the process of signing documents. Among other things, this
968 // includes the capability to allow for easy integration of PDF-specific signing-related operations
969 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
970 // to system keystores, etc.
971 try
972 {
973 CustomSigningAPI(input_path + "waiver.pdf",
974 "PDFTronApprovalSig",
975 input_path + "pdftron.pfx",
976 "password",
977 input_path + "pdftron.cer",
978 input_path + "signature.jpg",
979 Crypto::DigestAlgorithm::e_SHA256,
980 true,
981 output_path + "waiver_custom_signed.pdf");
982 }
983 catch (Common::Exception& e)
984 {
985 cerr << e << endl;
986 ret = 1;
987 }
988 catch (exception& e)
989 {
990 cerr << e.what() << "\n";
991 ret = 1;
992 }
993 catch (...)
994 {
995 cerr << "Unknown exception.\n";
996 ret = 1;
997 }
998
999 //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
1000 //try
1001 //{
1002 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
1003 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
1004 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
1005 // const UString tsa_url = "YOUR_URL_OF_TSA";
1006 // if (tsa_url == "YOUR_URL_OF_TSA")
1007 // {
1008 // throw exception("Error: The URL of your timestamp authority was not specified.");
1009 // }
1010 //
1011 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
1012 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
1013 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
1014 // const UString trusted_cert_path = "YOUR_CERTIFICATE";
1015 // if (trusted_cert_path == "YOUR_CERTIFICATE")
1016 // {
1017 // throw exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
1018 // }
1019 //
1020 // if (!TimestampAndEnableLTV(input_path + "waiver.pdf",
1021 // tsa_url,
1022 // trusted_cert_path,
1023 // input_path + "signature.jpg",
1024 // output_path+ "waiver_DocTimeStamp_LTV.pdf"))
1025 // {
1026 // ret = 1;
1027 // }
1028 //}
1029 //catch (Common::Exception& e)
1030 //{
1031 // cerr << e << endl;
1032 // ret = 1;
1033 //}
1034 //catch (exception& e)
1035 //{
1036 // cerr << e.what() << "\n";
1037 // ret = 1;
1038 //}
1039 //catch (...)
1040 //{
1041 // cerr << "Unknown exception.\n";
1042 // ret = 1;
1043 //}
1044
1045 //////////////////// End of tests. ////////////////////
1046
1047 if (!ret)
1048 {
1049 cout << "Tests successful." << endl << "==========" << endl;
1050 }
1051 else
1052 {
1053 cout << "Tests FAILED!!!" << endl << "==========" << endl;
1054 }
1055
1056 PDFNet::Terminate();
1057
1058#if (!USE_STD_SIGNATURE_HANDLER)
1059 ERR_free_strings();
1060 EVP_cleanup();
1061#endif // (!USE_STD_SIGNATURE_HANDLER)
1062
1063 return ret;
1064}
1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6//----------------------------------------------------------------------------------------------------------------------
7// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
8//
9// The following steps reflect typical intended usage of the digital signatures API:
10//
11// 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)).
12//
13// 1. EITHER:
14// (a) Call doc.createDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
15// -OR-
16// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
17// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.getField to get it by its fully qualified name.
18//
19// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
20// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
21// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
22//
23// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
24// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
25// certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
26// is signed.]
27//
28// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
29// a. Taking a PKCS #12 keyfile path and its password
30// b. Taking a buffer containing a PKCS #12 private keyfile and its password
31// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
32// in the following fashion:
33// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
34// validate/check a digital signature.
35// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
36// pdfdoc.addSignatureHandler(). The method returns a SignatureHandlerId.
37// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
38// NOTE: It is only possible to sign/certify one signature per call to the Save function.
39//
40// 5. Call pdfdoc.save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
41// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
42// so as to not invalidate the other signature(s).
43//
44// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
45// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
46// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
47//
48// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
49// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
50//----------------------------------------------------------------------------------------------------------------------
51
52import java.util.Locale;
53import java.io.IOException;
54import com.pdftron.common.PDFNetException;
55import com.pdftron.pdf.annots.TextWidget;
56import com.pdftron.pdf.annots.SignatureWidget;
57import com.pdftron.pdf.FieldIterator;
58import com.pdftron.pdf.DigitalSignatureFieldIterator;
59import com.pdftron.pdf.Field;
60import com.pdftron.pdf.Image;
61import com.pdftron.pdf.PDFDoc;
62import com.pdftron.pdf.PDFNet;
63import com.pdftron.pdf.Page;
64import com.pdftron.pdf.Rect;
65import com.pdftron.pdf.Date;
66import com.pdftron.pdf.DigitalSignatureField;
67import com.pdftron.pdf.VerificationOptions;
68import com.pdftron.pdf.VerificationResult;
69import com.pdftron.pdf.TrustVerificationResult;
70import com.pdftron.crypto.DigestAlgorithm;
71import com.pdftron.pdf.DisallowedChange;
72import com.pdftron.sdf.Obj;
73import com.pdftron.sdf.SignatureHandler;
74import com.pdftron.sdf.SDFDoc;
75import com.pdftron.filters.FilterReader;
76import com.pdftron.filters.MappedFile;
77import com.pdftron.crypto.X509Certificate;
78import com.pdftron.crypto.X501AttributeTypeAndValue;
79import com.pdftron.crypto.X501DistinguishedName;
80import com.pdftron.crypto.ObjectIdentifier;
81import com.pdftron.pdf.TimestampingConfiguration;
82import com.pdftron.pdf.TimestampingResult;
83import com.pdftron.pdf.EmbeddedTimestampVerificationResult;
84public class DigitalSignaturesTest
85{
86 public static boolean verifySimple(String in_docpath, String in_public_key_file_path) throws PDFNetException
87 {
88 PDFDoc doc = new PDFDoc(in_docpath);
89 System.out.println("==========");
90 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
91
92 // Add trust root to store of trusted certificates contained in VerificationOptions.
93 opts.addTrustedCertificate(in_public_key_file_path,
94 VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);
95
96 PDFDoc.SignaturesVerificationStatus result = doc.verifySignedDigitalSignatures(opts);
97
98 switch (result)
99 {
100 case e_unsigned:
101 System.out.println("Document has no signed signature fields.");
102 return false;
103 /*e_failure == bad doc status, digest status, or permissions status
104 (i.e. does not include trust issues, because those are flaky due to being network/config-related)*/
105 case e_failure:
106 System.out.println("Hard failure in verification on at least one signature.");
107 return false;
108 case e_untrusted:
109 System.out.println("Could not verify trust for at least one signature.");
110 return false;
111 case e_unsupported:
112 /*If necessary, call GetUnsupportedFeatures on VerificationResult to check which
113 unsupported features were encountered (requires verification using 'detailed' APIs) */
114 System.out.println("At least one signature contains unsupported features.");
115 return false;
116 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
117 case e_verified:
118 System.out.println("All signed signatures in document verified.");
119 return true;
120 default:
121 System.err.println("unrecognized document verification status");
122 assert(false);
123 }
124 return false;
125 }
126
127 public static boolean verifyAllAndPrint(String in_docpath, String in_public_key_file_path) throws PDFNetException
128 {
129 PDFDoc doc = new PDFDoc(in_docpath);
130 System.out.println("==========");
131 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
132
133 // Add trust root to store of trusted certificates contained in VerificationOptions.
134 opts.addTrustedCertificate(in_public_key_file_path,
135 VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);
136
137 // Iterate over the signatures and verify all of them.
138 DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
139 boolean verification_status = true;
140 for (; digsig_fitr.hasNext(); )
141 {
142 DigitalSignatureField curr = digsig_fitr.next();
143 VerificationResult result = curr.verify(opts);
144 if (result.getVerificationStatus())
145 {
146 System.out.print("Signature verified, ");
147 }
148 else
149 {
150 System.out.print("Signature verification failed, ");
151 verification_status = false;
152 }
153 System.out.println(String.format(Locale.US, "objnum: %d", curr.getSDFObj().getObjNum()));
154
155 switch (result.getDigestAlgorithm())
156 {
157 case e_sha1:
158 System.out.println("Digest algorithm: SHA-1");
159 break;
160 case e_sha256:
161 System.out.println("Digest algorithm: SHA-256");
162 break;
163 case e_sha384:
164 System.out.println("Digest algorithm: SHA-384");
165 break;
166 case e_sha512:
167 System.out.println("Digest algorithm: SHA-512");
168 break;
169 case e_ripemd160:
170 System.out.println("Digest algorithm: RIPEMD-160");
171 break;
172 case e_unknown_digest_algorithm:
173 System.out.println("Digest algorithm: unknown");
174 break;
175 default:
176 System.err.println("unrecognized digest algorithm");
177 assert(false);
178 }
179 System.out.println(String.format("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s",
180 result.getDocumentStatusAsString(),
181 result.getDigestStatusAsString(),
182 result.getTrustStatusAsString(),
183 result.getPermissionsStatusAsString()));
184
185
186 DisallowedChange[] changes = result.getDisallowedChanges();
187 for (DisallowedChange it2 : changes)
188 {
189 System.out.println(String.format(Locale.US, "\tDisallowed change: %s, objnum: %d", it2.getTypeAsString(), it2.getObjNum()));
190 }
191
192 // Get and print all the detailed trust-related results, if they are available.
193 if (result.hasTrustVerificationResult())
194 {
195 TrustVerificationResult trust_verification_result = result.getTrustVerificationResult();
196 System.out.println(trust_verification_result.wasSuccessful() ? "Trust verified." : "Trust not verifiable.");
197 System.out.println(trust_verification_result.getResultString());
198
199 long time_of_verification = trust_verification_result.getTimeOfTrustVerification();
200 switch (trust_verification_result.getTimeOfTrustVerificationEnum())
201 {
202 case e_current:
203 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to current time (as epoch time): %d", time_of_verification));
204 break;
205 case e_signing:
206 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to signing time (as epoch time): %d", time_of_verification));
207 break;
208 case e_timestamp:
209 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to secure embedded timestamp (as epoch time): %d", time_of_verification));
210 break;
211 default:
212 System.err.println("unrecognized time enum value");
213 assert(false);
214 }
215
216 if(trust_verification_result.getCertPath().length == 0 )
217 {
218 System.out.println("Could not print certificate path.");
219 }
220 else
221 {
222 System.out.println("Certificate path:");
223 X509Certificate[] cert_path = trust_verification_result.getCertPath();
224 for (int j = 0; j < cert_path.length; j++)
225 {
226 System.out.println("\tCertificate:");
227 X509Certificate full_cert = cert_path[j];
228 System.out.println("\t\tIssuer names:");
229
230 X501AttributeTypeAndValue[] issuer_dn = full_cert.getIssuerField().getAllAttributesAndValues();
231 for (int i = 0; i < issuer_dn.length; i++)
232 {
233 System.out.println("\t\t\t" + issuer_dn[i].getStringValue());
234 }
235 System.out.println("\t\tSubject names:");
236 X501AttributeTypeAndValue[] subject_dn = full_cert.getSubjectField().getAllAttributesAndValues();
237 for (int i = 0; i < subject_dn.length; i++)
238 {
239 System.out.println("\t\t\t" + subject_dn[i].getStringValue());
240 }
241 System.out.println("\t\tExtensions:");
242 for (int i = 0; i < full_cert.getExtensions().length; i++)
243 {
244 System.out.println("\t\t\t" + full_cert.getExtensions()[i].toString());
245 }
246 }
247 }
248 }
249 else
250 {
251 System.out.println("No detailed trust verification result available.");
252 }
253
254 String[] unsupported_features = result.getUnsupportedFeatures();
255 if (unsupported_features.length > 0)
256 {
257 System.out.println("Unsupported features:");
258
259 for (String unsupported_feature : unsupported_features)
260 {
261 System.out.println("\t" + unsupported_feature);
262 }
263 }
264 System.out.println("==========");
265 }
266
267 return verification_status;
268 }
269
270 public static void certifyPDF(String in_docpath,
271 String in_cert_field_name,
272 String in_private_key_file_path,
273 String in_keyfile_password,
274 String in_appearance_image_path,
275 String in_outpath) throws PDFNetException
276 {
277 System.out.println("================================================================================");
278 System.out.println("Certifying PDF document");
279
280 // Open an existing PDF
281 PDFDoc doc = new PDFDoc(in_docpath);
282
283 if (doc.hasSignatures())
284 {
285 System.out.println("PDFDoc has signatures");
286 }
287 else
288 {
289 System.out.println("PDFDoc has no signatures");
290 }
291
292 Page page1 = doc.getPage(1);
293
294 // Create a text field that we can lock using the field permissions feature.
295 TextWidget annot1 = TextWidget.create(doc, new Rect(143, 440, 350, 460), "asdf_test_field");
296 page1.annotPushBack(annot1);
297
298 /* Create a new signature form field in the PDFDoc. The name argument is optional;
299 leaving it empty causes it to be auto-generated. However, you may need the name for later.
300 Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
301 Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
302 DigitalSignatureField certification_sig_field = doc.createDigitalSignatureField(in_cert_field_name);
303 SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(143, 287, 219, 306), certification_sig_field);
304 page1.annotPushBack(widgetAnnot);
305
306 // (OPTIONAL) Add an appearance to the signature field.
307 Image img = Image.create(doc, in_appearance_image_path);
308 widgetAnnot.createSignatureAppearance(img);
309
310 // Prepare the document locking permission level. It will be applied upon document certification.
311 System.out.println("Adding document permissions.");
312 certification_sig_field.setDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
313
314 // Prepare to lock the text field that we created earlier.
315 System.out.println("Adding field permissions.");
316 String[] fields_to_lock = {"asdf_test_field"};
317 certification_sig_field.setFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);
318
319 certification_sig_field.certifyOnNextSave(in_private_key_file_path, in_keyfile_password);
320
321 // (OPTIONAL) Add more information to the signature dictionary.
322 certification_sig_field.setLocation("Vancouver, BC");
323 certification_sig_field.setReason("Document certification.");
324 certification_sig_field.setContactInfo("www.pdftron.com");
325
326 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
327 doc.save(in_outpath, SDFDoc.SaveMode.NO_FLAGS, null);
328
329 System.out.println("================================================================================");
330 }
331
332 public static void signPDF(String in_docpath,
333 String in_approval_field_name,
334 String in_private_key_file_path,
335 String in_keyfile_password,
336 String in_appearance_img_path,
337 String in_outpath) throws PDFNetException
338 {
339 System.out.println("================================================================================");
340 System.out.println("Signing PDF document");
341
342 // Open an existing PDF
343 PDFDoc doc = new PDFDoc(in_docpath);
344
345 // Retrieve the unsigned approval signature field.
346 Field found_approval_field = doc.getField(in_approval_field_name);
347 DigitalSignatureField found_approval_signature_digsig_field = new DigitalSignatureField(found_approval_field);
348
349 // (OPTIONAL) Add an appearance to the signature field.
350 Image img = Image.create(doc, in_appearance_img_path);
351 SignatureWidget found_approval_signature_widget = new SignatureWidget(found_approval_field.getSDFObj());
352 found_approval_signature_widget.createSignatureAppearance(img);
353
354 // Prepare the signature and signature handler for signing.
355 found_approval_signature_digsig_field.signOnNextSave(in_private_key_file_path, in_keyfile_password);
356
357 // The actual approval signing will be done during the following incremental save operation.
358 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
359
360 System.out.println("================================================================================");
361 }
362
363 public static void clearSignature(String in_docpath,
364 String in_digsig_field_name,
365 String in_outpath) throws PDFNetException
366 {
367 System.out.println("================================================================================");
368 System.out.println("Clearing certification signature");
369
370 PDFDoc doc = new PDFDoc(in_docpath);
371
372 DigitalSignatureField digsig = new DigitalSignatureField(doc.getField(in_digsig_field_name));
373
374 System.out.println("Clearing signature: " + in_digsig_field_name);
375 digsig.clearSignature();
376
377 if (!digsig.hasCryptographicSignature())
378 {
379 System.out.println("Cryptographic signature cleared properly.");
380 }
381
382 // Save incrementally so as to not invalidate other signatures from previous saves.
383 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
384
385 System.out.println("================================================================================");
386 }
387
388 public static void printSignaturesInfo(String in_docpath) throws PDFNetException
389 {
390 System.out.println("================================================================================");
391 System.out.println("Reading and printing digital signature information");
392
393 PDFDoc doc = new PDFDoc(in_docpath);
394 if (!doc.hasSignatures())
395 {
396 System.out.println("Doc has no signatures.");
397 System.out.println("================================================================================");
398 return;
399 }
400 else
401 {
402 System.out.println("Doc has signatures.");
403 }
404
405
406 for (FieldIterator fitr = doc.getFieldIterator(); fitr.hasNext(); )
407 {
408 Field current = fitr.next();
409 if (current.isLockedByDigitalSignature())
410 {
411 System.out.println("==========\nField locked by a digital signature");
412 }
413 else
414 {
415 System.out.println("==========\nField not locked by a digital signature");
416 }
417
418 System.out.println("Field name: " + current.getName());
419 System.out.println("==========");
420 }
421
422 System.out.println("====================\nNow iterating over digital signatures only.\n====================");
423
424 DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
425 for (; digsig_fitr.hasNext(); )
426 {
427 DigitalSignatureField current = digsig_fitr.next();
428 System.out.println("==========");
429 System.out.println("Field name of digital signature: " + new Field(current.getSDFObj()).getName());
430
431 DigitalSignatureField digsigfield = current;
432 if (!digsigfield.hasCryptographicSignature())
433 {
434 System.out.println("Either digital signature field lacks a digital signature dictionary, " +
435 "or digital signature dictionary lacks a cryptographic Contents entry. " +
436 "Digital signature field is not presently considered signed.\n" +
437 "==========");
438 continue;
439 }
440
441 int cert_count = digsigfield.getCertCount();
442 System.out.println("Cert count: " + cert_count);
443 for (int i = 0; i < cert_count; ++i)
444 {
445 byte[] cert = digsigfield.getCert(i);
446 System.out.println("Cert #" + i + " size: " + cert.length);
447 }
448
449 DigitalSignatureField.SubFilterType subfilter = digsigfield.getSubFilter();
450
451 System.out.println("Subfilter type: " + subfilter.ordinal());
452
453 if (subfilter != DigitalSignatureField.SubFilterType.e_ETSI_RFC3161)
454 {
455 System.out.println("Signature's signer: " + digsigfield.getSignatureName());
456
457 Date signing_time = digsigfield.getSigningTime();
458 if (signing_time.isValid())
459 {
460 System.out.println("Signing time is valid.");
461 }
462
463 System.out.println("Location: " + digsigfield.getLocation());
464 System.out.println("Reason: " + digsigfield.getReason());
465 System.out.println("Contact info: " + digsigfield.getContactInfo());
466 }
467 else
468 {
469 System.out.println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)");
470 }
471
472 if (digsigfield.hasVisibleAppearance())
473 {
474 System.out.println("Visible");
475 }
476 else
477 {
478 System.out.println("Not visible");
479 }
480
481 DigitalSignatureField.DocumentPermissions digsig_doc_perms = digsigfield.getDocumentPermissions();
482 String[] locked_fields = digsigfield.getLockedFields();
483 for (String it : locked_fields)
484 {
485 System.out.println("This digital signature locks a field named: " + it);
486 }
487
488 switch (digsig_doc_perms)
489 {
490 case e_no_changes_allowed:
491 System.out.println("No changes to the document can be made without invalidating this digital signature.");
492 break;
493 case e_formfilling_signing_allowed:
494 System.out.println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
495 break;
496 case e_annotating_formfilling_signing_allowed:
497 System.out.println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
498 break;
499 case e_unrestricted:
500 System.out.println("Document not restricted by this digital signature.");
501 break;
502 default:
503 System.err.println("Unrecognized digital signature document permission level.");
504 assert(false);
505 }
506 System.out.println("==========");
507 }
508
509 System.out.println("================================================================================");
510 }
511
512 public static void CustomSigningAPI(String doc_path,
513 String cert_field_name,
514 String private_key_file_path,
515 String keyfile_password,
516 String public_key_file_path,
517 String appearance_image_path,
518 DigestAlgorithm digest_algorithm_type,
519 boolean PAdES_signing_mode,
520 String output_path) throws PDFNetException, IOException
521 {
522 System.out.println("================================================================================");
523 System.out.println("Custom signing PDF document");
524
525 PDFDoc doc = new PDFDoc(doc_path);
526
527 Page page1 = doc.getPage(1);
528
529 DigitalSignatureField digsig_field = doc.createDigitalSignatureField(cert_field_name);
530 SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(143, 287, 219, 306), digsig_field);
531 page1.annotPushBack(widgetAnnot);
532
533 // (OPTIONAL) Add an appearance to the signature field.
534 Image img = Image.create(doc, appearance_image_path);
535 widgetAnnot.createSignatureAppearance(img);
536
537 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
538 digsig_field.createSigDictForCustomSigning("Adobe.PPKLite",
539 PAdES_signing_mode ? DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached : DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached,
540 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.
541 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
542
543 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
544 Date current_date = new Date();
545 current_date.setCurrentTime();
546 digsig_field.setSigDictTimeOfSigning(current_date);
547
548 doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);
549
550 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
551 byte[] pdf_digest = digsig_field.calculateDigest(digest_algorithm_type);
552
553 X509Certificate signer_cert = new X509Certificate(public_key_file_path);
554
555 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
556 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
557 byte[] pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.generateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);
558
559 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
560 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
561 byte[] signedAttrs = DigitalSignatureField.generateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute);
562
563 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
564 byte[] signedAttrs_digest = DigestAlgorithm.calculateDigest(digest_algorithm_type, signedAttrs);
565
566 //////////////////////////// custom digest signing starts ////////////////////////////
567 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
568 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
569 byte[] signature_value = DigestAlgorithm.signDigest(
570 signedAttrs_digest,
571 digest_algorithm_type,
572 private_key_file_path,
573 keyfile_password);
574 //////////////////////////// custom digest signing ends //////////////////////////////
575
576 // Then, load all your chain certificates into a container of X509Certificate.
577 X509Certificate[] chain_certs = {};
578
579 // Then, create ObjectIdentifiers for the algorithms you have used.
580 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
581 ObjectIdentifier digest_algorithm_oid = new ObjectIdentifier(digest_algorithm_type);
582 ObjectIdentifier signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier.Predefined.RSA_encryption_PKCS1);
583
584 // Then, put the CMS signature components together.
585 byte[] cms_signature = DigitalSignatureField.generateCMSSignature(
586 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
587 signature_value, signedAttrs);
588
589 // Write the signature to the document.
590 doc.saveCustomSignature(cms_signature, digsig_field, output_path);
591
592 System.out.println("================================================================================");
593 }
594
595 public static boolean timestampAndEnableLTV(String doc_path,
596 String tsa_url,
597 String trusted_cert_path,
598 String appearance_img_path,
599 String output_path) throws PDFNetException
600 {
601 PDFDoc doc = new PDFDoc(doc_path);
602 DigitalSignatureField doctimestamp_signature_field = doc.createDigitalSignatureField();
603 TimestampingConfiguration tst_config = new TimestampingConfiguration(tsa_url);
604 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
605 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
606 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
607 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
608 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
609 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
610 opts.addTrustedCertificate(trusted_cert_path);
611 /* By default, we only check online for revocation of certificates using the newer and lighter
612 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
613 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
614 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
615 opts.enableOnlineCRLRevocationChecking(true);
616
617 SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(0, 100, 200, 150), doctimestamp_signature_field);
618 doc.getPage(1).annotPushBack(widgetAnnot);
619
620 // (OPTIONAL) Add an appearance to the signature field.
621 Image img = Image.create(doc, appearance_img_path);
622 widgetAnnot.createSignatureAppearance(img);
623
624 System.out.println("Testing timestamping configuration.");
625 TimestampingResult config_result = tst_config.testConfiguration(opts);
626 if (config_result.getStatus())
627 {
628 System.out.println("Success: timestamping configuration usable. Attempting to timestamp.");
629 }
630 else
631 {
632 // Print details of timestamping failure.
633 System.out.println(config_result.getString());
634 if (config_result.hasResponseVerificationResult())
635 {
636 EmbeddedTimestampVerificationResult tst_result = config_result.getResponseVerificationResult();
637 System.out.println(String.format("CMS digest status: %s", tst_result.getCMSDigestStatusAsString()));
638 System.out.println(String.format("Message digest status: %s", tst_result.getMessageImprintDigestStatusAsString()));
639 System.out.println(String.format("Trust status: %s", tst_result.getTrustStatusAsString()));
640 }
641 return false;
642 }
643
644 doctimestamp_signature_field.timestampOnNextSave(tst_config, opts);
645
646 // Save/signing throws if timestamping fails.
647 doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);
648
649 System.out.println("Timestamping successful. Adding LTV information for DocTimeStamp signature.");
650
651 // Add LTV information for timestamp signature to document.
652 VerificationResult timestamp_verification_result = doctimestamp_signature_field.verify(opts);
653 if (!doctimestamp_signature_field.enableLTVOfflineVerification(timestamp_verification_result))
654 {
655 System.out.println("Could not enable LTV for DocTimeStamp.");
656 return false;
657 }
658 doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);
659 System.out.println("Added LTV information for DocTimeStamp signature successfully.");
660
661 return true;
662 }
663
664 public static void main(String[] args)
665 {
666 // Initialize PDFNet
667 PDFNet.initialize(PDFTronLicense.Key());
668
669 boolean result = true;
670 String input_path = "../../TestFiles/";
671 String output_path = "../../TestFiles/Output/";
672
673
674 //////////////////// TEST 0:
675 /* Create an approval signature field that we can sign after certifying.
676 (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
677 try (PDFDoc doc = new PDFDoc(input_path + "waiver.pdf"))
678 {
679 DigitalSignatureField approval_signature_field = doc.createDigitalSignatureField("PDFTronApprovalSig");
680 SignatureWidget widgetAnnotApproval = SignatureWidget.create(doc, new Rect(300, 287, 376, 306), approval_signature_field);
681 Page page1 = doc.getPage(1);
682 page1.annotPushBack(widgetAnnotApproval);
683 doc.save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc.SaveMode.REMOVE_UNUSED, null);
684 }
685 catch (Exception e)
686 {
687 System.err.println(e.getMessage());
688 e.printStackTrace(System.err);
689 result = false;
690 }
691
692 //////////////////// TEST 1: certify a PDF.
693 try
694 {
695 certifyPDF(input_path + "waiver_withApprovalField.pdf",
696 "PDFTronCertificationSig",
697 input_path + "pdftron.pfx",
698 "password",
699 input_path + "pdftron.bmp",
700 output_path + "waiver_withApprovalField_certified_output.pdf");
701 printSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
702 }
703 catch (Exception e)
704 {
705 System.err.println(e.getMessage());
706 e.printStackTrace(System.err);
707 result = false;
708 }
709
710 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
711 try
712 {
713 signPDF(input_path + "waiver_withApprovalField_certified.pdf",
714 "PDFTronApprovalSig",
715 input_path + "pdftron.pfx",
716 "password",
717 input_path + "signature.jpg",
718 output_path + "waiver_withApprovalField_certified_approved_output.pdf");
719 printSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
720 }
721 catch (Exception e)
722 {
723 System.err.println(e.getMessage());
724 e.printStackTrace(System.err);
725 result = false;
726 }
727
728 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
729 try
730 {
731 clearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
732 "PDFTronCertificationSig",
733 output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
734 printSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
735 }
736 catch (Exception e)
737 {
738 System.err.println(e.getMessage());
739 e.printStackTrace(System.err);
740 result = false;
741 }
742
743 //////////////////// TEST 4: Verify a document's digital signatures.
744 try
745 {
746 if (!verifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf",
747 input_path + "pdftron.cer"))
748 {
749 result = false;
750 }
751 }
752 catch (Exception e)
753 {
754 System.err.println(e.getMessage());
755 e.printStackTrace(System.err);
756 result = false;
757 }
758
759 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
760 try
761 {
762 if (!verifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf",
763 input_path + "pdftron.cer"))
764 {
765 result = false;
766 }
767 }
768 catch (Exception e)
769 {
770 System.err.println(e.getMessage());
771 e.printStackTrace(System.err);
772 result = false;
773
774 }
775
776 //////////////////// TEST 6: Custom signing API.
777 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
778 // which allows users to customize the process of signing documents. Among other things, this
779 // includes the capability to allow for easy integration of PDF-specific signing-related operations
780 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
781 // to system keystores, etc.
782 try
783 {
784 CustomSigningAPI(input_path + "waiver.pdf",
785 "PDFTronApprovalSig",
786 input_path + "pdftron.pfx",
787 "password",
788 input_path + "pdftron.cer",
789 input_path + "signature.jpg",
790 DigestAlgorithm.e_sha256,
791 true,
792 output_path + "waiver_custom_signed.pdf");
793 }
794 catch (Exception e)
795 {
796 System.err.println(e.getMessage());
797 e.printStackTrace(System.err);
798 result = false;
799 }
800
801 //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
802 // try
803 // {
804 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
805 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
806 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
807 // String tsa_url = "YOUR_URL_OF_TSA";
808 // if (tsa_url == "YOUR_URL_OF_TSA")
809 // {
810 // throw new Exception("Error: The URL of your timestamp authority was not specified.");
811 // }
812 //
813 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
814 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
815 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
816 // String trusted_cert_path = "YOUR_CERTIFICATE";
817 // if (trusted_cert_path == "YOUR_CERTIFICATE")
818 // {
819 // throw new Exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
820 // }
821 //
822 // if (!timestampAndEnableLTV(input_path + "waiver.pdf",
823 // tsa_url,
824 // trusted_cert_path,
825 // input_path + "signature.jpg",
826 // output_path+ "waiver_DocTimeStamp_LTV.pdf"))
827 // {
828 // result = false;
829 // }
830 // }
831 // catch (Exception e)
832 // {
833 // System.err.println(e.getMessage());
834 // e.printStackTrace(System.err);
835 // result = false;
836 //
837 // }
838
839 //////////////////// End of tests. ////////////////////
840
841 if (result)
842 {
843 System.out.println("Tests successful.\n==========");
844 }
845 else
846 {
847 System.out.println("Tests FAILED!!!\n==========");
848 }
849 PDFNet.terminate();
850 }
851}
1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6
7const { PDFNet } = require('@pdftron/pdfnet-node');
8const PDFTronLicense = require('../LicenseKey/LicenseKey');
9
10((exports) => {
11 'use strict';
12
13 exports.runDigitalSignatureTest = () => {
14
15 const input_path = '../TestFiles/';
16 const output_path = '../TestFiles/Output/';
17
18 const VerifySimple = async (in_docpath, in_public_key_file_path) => {
19 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
20 doc.initSecurityHandler();
21 console.log('==========');
22 const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
23
24 // Add trust root to store of trusted certificates contained in VerificationOptions.
25 await opts.addTrustedCertificateUString(in_public_key_file_path,
26 PDFNet.VerificationOptions.CertificateTrustFlag.e_default_trust + PDFNet.VerificationOptions.CertificateTrustFlag.e_certification_trust);
27
28 const result = await doc.verifySignedDigitalSignatures(opts);
29 switch (result) {
30 case PDFNet.PDFDoc.SignaturesVerificationStatus.e_unsigned:
31 console.log('Document has no signed signature fields.');
32 return false;
33 /* e_failure == bad doc status, digest status, or permissions status
34 (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
35 case PDFNet.PDFDoc.SignaturesVerificationStatus.e_failure:
36 console.log('Hard failure in verification on at least one signature.');
37 return false;
38 case PDFNet.PDFDoc.SignaturesVerificationStatus.e_untrusted:
39 console.log('Could not verify trust for at least one signature.');
40 return false;
41 case PDFNet.PDFDoc.SignaturesVerificationStatus.e_unsupported:
42 /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
43 unsupported features were encountered (requires verification using 'detailed' APIs) */
44 console.log('At least one signature contains unsupported features.');
45 return false;
46 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
47 case PDFNet.PDFDoc.SignaturesVerificationStatus.e_verified:
48 console.log('All signed signatures in document verified.');
49 return true;
50 }
51
52 return false;
53 }
54
55 const VerifyAllAndPrint = async (in_docpath, in_public_key_file_path) => {
56 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
57 doc.initSecurityHandler();
58 console.log('==========');
59 const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
60
61 // Add trust root to store of trusted certificates contained in VerificationOptions.
62 // Use trust level corresponding to an identity trusted even for certification signatures.
63 await opts.addTrustedCertificateUString(in_public_key_file_path,
64 PDFNet.VerificationOptions.CertificateTrustFlag.e_default_trust + PDFNet.VerificationOptions.CertificateTrustFlag.e_certification_trust);
65
66 // Iterate over the signatures and verify all of them.
67 const digsig_fitr = await doc.getDigitalSignatureFieldIteratorBegin();
68 var verification_status = true;
69 for (; await digsig_fitr.hasNext(); await digsig_fitr.next()) {
70 const curr = await digsig_fitr.current();
71 const result = await curr.verify(opts);
72 if (await result.getVerificationStatus()) {
73 console.log('Signature verified, objnum: ' + await (await curr.getSDFObj()).getObjNum());
74 } else {
75 console.log('Signature verification failed, objnum: ' + await (await curr.getSDFObj()).getObjNum());
76 verification_status = false;
77 }
78
79 switch (await result.getDigestAlgorithm()) {
80 case PDFNet.DigestAlgorithm.Type.e_SHA1:
81 console.log('Digest algorithm: SHA-1');
82 break;
83 case PDFNet.DigestAlgorithm.Type.e_SHA256:
84 console.log('Digest algorithm: SHA-256');
85 break;
86 case PDFNet.DigestAlgorithm.Type.e_SHA384:
87 console.log('Digest algorithm: SHA-384');
88 break;
89 case PDFNet.DigestAlgorithm.Type.e_SHA512:
90 console.log('Digest algorithm: SHA-512');
91 break;
92 case PDFNet.DigestAlgorithm.Type.e_RIPEMD160:
93 console.log('Digest algorithm: RIPEMD-160');
94 break;
95 case PDFNet.DigestAlgorithm.Type.e_unknown_digest_algorithm:
96 console.log('Digest algorithm: unknown');
97 break;
98 }
99
100 console.log('Detailed verification result: \n\t'
101 + await result.getDocumentStatusAsString() + '\n\t'
102 + await result.getDigestStatusAsString() + '\n\t'
103 + await result.getTrustStatusAsString() + '\n\t'
104 + await result.getPermissionsStatusAsString());
105
106 const changes = await result.getDisallowedChanges();
107 for (var i = 0; i < changes.length; ++i) {
108 const change = changes[i];
109 console.log('\tDisallowed change: ' + await change.getTypeAsString() + ', objnum: ' + await change.getObjNum());
110 }
111
112 // Get and print all the detailed trust-related results, if they are available.
113 if (await result.hasTrustVerificationResult()) {
114 const trust_verification_result = await result.getTrustVerificationResult();
115 console.log(await trust_verification_result.wasSuccessful() ? 'Trust verified.' : 'Trust not verifiable.');
116 console.log(await trust_verification_result.getResultString());
117
118 const tmp_time_t = await trust_verification_result.getTimeOfTrustVerification();
119 switch (await trust_verification_result.getTimeOfTrustVerificationEnum()) {
120 case PDFNet.VerificationOptions.TimeMode.e_current:
121 console.log('Trust verification attempted with respect to current time (as epoch time):' + tmp_time_t);
122 break;
123 case PDFNet.VerificationOptions.TimeMode.e_signing:
124 console.log('Trust verification attempted with respect to signing time (as epoch time): ' + tmp_time_t);
125 break;
126 case PDFNet.VerificationOptions.TimeMode.e_timestamp:
127 console.log('Trust verification attempted with respect to secure embedded timestamp (as epoch time): ' + tmp_time_t);
128 break;
129 }
130
131 const cert_path = await trust_verification_result.getCertPath();
132 if (cert_path.length == 0) {
133 console.log('Could not print certificate path.');
134 } else {
135 console.log('Certificate path:');
136 for (var i = 0; i < cert_path.length; i++) {
137 console.log('\tCertificate:');
138 const full_cert = cert_path[i];
139 console.log('\t\tIssuer names:');
140 const issuer_dn = await (await full_cert.getIssuerField()).getAllAttributesAndValues();
141 for (var j = 0; j < issuer_dn.length; j++) {
142 console.log('\t\t\t' + await issuer_dn[j].getStringValue());
143 }
144 console.log('\t\tSubject names:');
145 const subject_dn = await (await full_cert.getSubjectField()).getAllAttributesAndValues();
146 for (var j = 0; j < subject_dn.length; j++) {
147 console.log('\t\t\t' + await subject_dn[j].getStringValue());
148 }
149 console.log('\t\tExtensions:');
150 const extension_dn = await full_cert.getExtensions();
151 for (var j = 0; j < extension_dn.length; j++) {
152 console.log('\t\t\t' + await extension_dn[j].toString());
153 }
154 }
155 }
156 }
157 else {
158 console.log('No detailed trust verification result available.');
159 }
160
161 console.log('==========');
162 }
163
164 return verification_status;
165 }
166
167 const CertifyPDF = async (in_docpath, in_cert_field_name, in_private_key_file_path, in_keyfile_password, in_appearance_image_path, in_outpath) => {
168 console.log('================================================================================');
169 console.log('Certifying PDF document');
170
171 // Open existing PDF.
172 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
173 doc.initSecurityHandler();
174
175 console.log('PDFDoc has ' + (await doc.hasSignatures() ? 'signatures' : 'no signatures'));
176
177 const page1 = await doc.getPage(1);
178
179 // Create a text field that we can lock using the field permissions feature.
180 const annot1 = await PDFNet.TextWidget.create(doc, new PDFNet.Rect(143, 440, 350, 460), 'asdf_test_field');
181 await page1.annotPushBack(annot1);
182
183 /* Create a new signature form field in the PDFDoc. The name argument is optional;
184 leaving it empty causes it to be auto-generated. However, you may need the name for later.
185 Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
186 Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
187 const certification_sig_field = await doc.createDigitalSignatureField(in_cert_field_name);
188 const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(143, 287, 219, 306), certification_sig_field);
189 await page1.annotPushBack(widgetAnnot);
190
191 // (OPTIONAL) Add an appearance to the signature field.
192 const img = await PDFNet.Image.createFromFile(doc, in_appearance_image_path);
193 await widgetAnnot.createSignatureAppearance(img);
194
195 // Prepare the document locking permission level. It will be applied upon document certification.
196 console.log('Adding document permissions.');
197 await certification_sig_field.setDocumentPermissions(PDFNet.DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
198
199 // Prepare to lock the text field that we created earlier.
200 console.log('Adding field permissions.');
201 var fields_to_lock = ['asdf_test_field'];
202 await certification_sig_field.setFieldPermissions(PDFNet.DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);
203
204 await certification_sig_field.certifyOnNextSave(in_private_key_file_path, in_keyfile_password);
205
206 // (OPTIONAL) Add more information to the signature dictionary.
207 await certification_sig_field.setLocation('Vancouver, BC');
208 await certification_sig_field.setReason('Document certification.');
209 await certification_sig_field.setContactInfo('www.pdftron.com');
210
211 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
212 await doc.save(in_outpath, 0);
213
214 console.log('================================================================================');
215 }
216
217 const SignPDF = async (in_docpath, in_approval_field_name, in_private_key_file_path, in_keyfile_password, in_appearance_img_path, in_outpath) => {
218 console.log('================================================================================');
219 console.log('Signing PDF document');
220
221 // Open an existing PDF
222 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
223 doc.initSecurityHandler();
224
225 // Retrieve the unsigned approval signature field.
226 const found_approval_field = await doc.getField(in_approval_field_name);
227 const found_approval_signature_digsig_field = await PDFNet.DigitalSignatureField.createFromField(found_approval_field);
228
229 // (OPTIONAL) Add an appearance to the signature field.
230 const img = await PDFNet.Image.createFromFile(doc, in_appearance_img_path);
231 const found_approval_signature_widget = await PDFNet.SignatureWidget.createFromObj(await found_approval_field.getSDFObj());
232 await found_approval_signature_widget.createSignatureAppearance(img);
233
234 // Prepare the signature and signature handler for signing.
235 await found_approval_signature_digsig_field.signOnNextSave(in_private_key_file_path, in_keyfile_password);
236
237 // The actual approval signing will be done during the following incremental save operation.
238 await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);
239
240 console.log('================================================================================');
241 }
242
243 const ClearSignature = async (in_docpath, in_digsig_field_name, in_outpath) => {
244 console.log('================================================================================');
245 console.log('Clearing certification signature');
246
247 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
248 doc.initSecurityHandler();
249
250 const digsig = await PDFNet.DigitalSignatureField.createFromField(await doc.getField(in_digsig_field_name));
251
252 console.log('Clearing signature: ' + in_digsig_field_name);
253 await digsig.clearSignature();
254
255 if (!(await digsig.hasCryptographicSignature())) {
256 console.log('Cryptographic signature cleared properly.');
257 }
258
259 // Save incrementally so as to not invalidate other signatures from previous saves.
260 await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);
261
262 console.log('================================================================================');
263 }
264
265 const PrintSignaturesInfo = async (in_docpath) => {
266 console.log('================================================================================');
267 console.log('Reading and printing digital signature information');
268
269 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
270 doc.initSecurityHandler();
271 if (!(await doc.hasSignatures())) {
272 console.log('Doc has no signatures.');
273 console.log('================================================================================');
274 return;
275 } else {
276 console.log('Doc has signatures.');
277 }
278
279
280 for (const fitr = await doc.getFieldIteratorBegin(); await fitr.hasNext(); await fitr.next()) {
281 const field = await fitr.current();
282 (await field.isLockedByDigitalSignature()) ? console.log('==========\nField locked by a digital signature') :
283 console.log('==========\nField not locked by a digital signature');
284
285 console.log('Field name: ' + await field.getName());
286 console.log('==========');
287 }
288
289 console.log('====================\nNow iterating over digital signatures only.\n====================');
290
291 const digsig_fitr = await doc.getDigitalSignatureFieldIteratorBegin();
292 for (; await digsig_fitr.hasNext(); await digsig_fitr.next()) {
293 console.log('==========');
294 const digsigfield = await digsig_fitr.current();
295 console.log('Field name of digital signature: ' + await (await PDFNet.Field.create(await digsigfield.getSDFObj())).getName());
296
297 if (!(await digsigfield.hasCryptographicSignature())) {
298 console.log('Either digital signature field lacks a digital signature dictionary, ' +
299 'or digital signature dictionary lacks a cryptographic Contents entry. ' +
300 'Digital signature field is not presently considered signed.\n' +
301 '==========');
302 continue;
303 }
304
305 const cert_count = await digsigfield.getCertCount();
306 console.log('Cert count: ' + cert_count);
307 for (var i = 0; i < cert_count; i++) {
308 const cert = await digsigfield.getCert(i);
309 console.log('Cert #' + i + ' size: ' + cert.byteLength);
310 }
311
312 const subfilter = await digsigfield.getSubFilter();
313
314 console.log('Subfilter type: ' + subfilter);
315
316 if (subfilter != PDFNet.DigitalSignatureField.SubFilterType.e_ETSI_RFC3161) {
317 console.log("Signature's signer: " + await digsigfield.getSignatureName());
318
319 const signing_time = await digsigfield.getSigningTime();
320 if (await signing_time.isValid()) {
321 console.log('Signing time is valid.');
322 }
323
324 console.log('Location: ' + await digsigfield.getLocation());
325 console.log('Reason: ' + await digsigfield.getReason());
326 console.log('Contact info: ' + await digsigfield.getContactInfo());
327 } else {
328 console.log('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)');
329 }
330
331 console.log((await digsigfield.hasVisibleAppearance()) ? 'Visible' : 'Not visible');
332
333 const digsig_doc_perms = await digsigfield.getDocumentPermissions();
334 const locked_fields = await digsigfield.getLockedFields();
335 for (var i = 0; i < locked_fields.length; i++) {
336 console.log('This digital signature locks a field named: ' + locked_fields[i]);
337 }
338
339 switch (digsig_doc_perms) {
340 case PDFNet.DigitalSignatureField.DocumentPermissions.e_no_changes_allowed:
341 console.log('No changes to the document can be made without invalidating this digital signature.');
342 break;
343 case PDFNet.DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed:
344 console.log('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
345 break;
346 case PDFNet.DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed:
347 console.log('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
348 break;
349 case PDFNet.DigitalSignatureField.DocumentPermissions.e_unrestricted:
350 console.log('Document not restricted by this digital signature.');
351 break;
352 }
353 console.log('==========');
354 }
355
356 console.log('================================================================================');
357 }
358
359 const CustomSigningAPI = async (doc_path, cert_field_name, private_key_file_path, keyfile_password, public_key_file_path, appearance_image_path, digest_algorithm_type, PAdES_signing_mode, output_path) => {
360 console.log('================================================================================');
361 console.log('Custom signing PDF document');
362
363 const doc = await PDFNet.PDFDoc.createFromFilePath(doc_path);
364 doc.initSecurityHandler();
365
366 const page1 = await doc.getPage(1);
367
368 const digsig_field = await doc.createDigitalSignatureField(cert_field_name);
369 const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(143, 287, 219, 306), digsig_field);
370 await page1.annotPushBack(widgetAnnot);
371
372 // (OPTIONAL) Add an appearance to the signature field.
373 const img = await PDFNet.Image.createFromFile(doc, appearance_image_path);
374 await widgetAnnot.createSignatureAppearance(img);
375
376 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
377 await digsig_field.createSigDictForCustomSigning('Adobe.PPKLite',
378 PAdES_signing_mode ? PDFNet.DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached : PDFNet.DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached,
379 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.
380 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
381
382 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
383 const current_date = new PDFNet.Date();
384 await current_date.setCurrentTime();
385 await digsig_field.setSigDictTimeOfSigning(current_date);
386
387 await doc.save(output_path, PDFNet.SDFDoc.SaveOptions.e_incremental);
388
389 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
390 const pdf_digest = await digsig_field.calculateDigest(digest_algorithm_type);
391
392 const signer_cert = await PDFNet.X509Certificate.createFromFile(public_key_file_path);
393
394 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
395 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
396 const pades_versioned_ess_signing_cert_attribute = await PDFNet.DigitalSignatureField.generateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);
397
398 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
399 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
400 const signedAttrs = await PDFNet.DigitalSignatureField.generateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute);
401
402 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
403 const signedAttrs_digest = await PDFNet.DigestAlgorithm.calculateDigest(digest_algorithm_type, signedAttrs);
404
405 //////////////////////////// custom digest signing starts ////////////////////////////
406 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
407 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
408 const signature_value = await PDFNet.DigestAlgorithm.signDigest(
409 signedAttrs_digest,
410 digest_algorithm_type,
411 private_key_file_path,
412 keyfile_password);
413 //////////////////////////// custom digest signing ends //////////////////////////////
414
415 // Then, load all your chain certificates into a container of X509Certificate.
416 const chain_certs = [];
417
418 // Then, create ObjectIdentifiers for the algorithms you have used.
419 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
420 const digest_algorithm_oid = await PDFNet.ObjectIdentifier.createFromDigestAlgorithm(digest_algorithm_type);
421 const signature_algorithm_oid = await PDFNet.ObjectIdentifier.createFromPredefined(PDFNet.ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1);
422
423 // Then, put the CMS signature components together.
424 const cms_signature = await PDFNet.DigitalSignatureField.generateCMSSignature(
425 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid, signature_value, signedAttrs);
426
427 // Write the signature to the document.
428 await doc.saveCustomSignature(cms_signature, digsig_field, output_path);
429
430 console.log('================================================================================');
431 };
432
433 const TimestampAndEnableLTV = async (in_docpath, in_tsa_url, in_trusted_cert_path, in_appearance_img_path, in_outpath) => {
434 const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
435 doc.initSecurityHandler();
436 const doctimestamp_signature_field = await doc.createDigitalSignatureField();
437 const tst_config = await PDFNet.TimestampingConfiguration.createFromURL(in_tsa_url);
438 const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
439 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
440 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
441 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
442 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
443 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
444 await opts.addTrustedCertificateUString(in_trusted_cert_path);
445 /* By default, we only check online for revocation of certificates using the newer and lighter
446 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
447 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
448 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
449 await opts.enableOnlineCRLRevocationChecking(true);
450
451 const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(0, 100, 200, 150), doctimestamp_signature_field);
452 await (await doc.getPage(1)).annotPushBack(widgetAnnot);
453
454 // (OPTIONAL) Add an appearance to the signature field.
455 const img = await PDFNet.Image.createFromFile(doc, in_appearance_img_path);
456 await widgetAnnot.createSignatureAppearance(img);
457
458 console.log('Testing timestamping configuration.');
459 const config_result = await tst_config.testConfiguration(opts);
460 if (await config_result.getStatus()) {
461 console.log('Success: timestamping configuration usable. Attempting to timestamp.');
462 } else {
463 // Print details of timestamping failure.
464 console.log(await config_result.getString());
465 if (await config_result.hasResponseVerificationResult()) {
466 const tst_result = await config_result.getResponseVerificationResult();
467 console.log('CMS digest status: ' + await tst_result.getCMSDigestStatusAsString());
468 console.log('Message digest status: ' + await tst_result.getMessageImprintDigestStatusAsString());
469 console.log('Trust status: ' + await tst_result.getTrustStatusAsString());
470 }
471 return false;
472 }
473
474 await doctimestamp_signature_field.timestampOnNextSave(tst_config, opts);
475
476 // Save/signing throws if timestamping fails.
477 await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);
478
479 console.log('Timestamping successful. Adding LTV information for DocTimeStamp signature.');
480
481 // Add LTV information for timestamp signature to document.
482 const timestamp_verification_result = await doctimestamp_signature_field.verify(opts);
483 if (!(await doctimestamp_signature_field.enableLTVOfflineVerification(timestamp_verification_result))) {
484 console.log('Could not enable LTV for DocTimeStamp.');
485 return false;
486 }
487 await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);
488 console.log('Added LTV information for DocTimeStamp signature successfully.');
489
490 return true;
491 }
492
493 const main = async () => {
494 var ret = 0;
495
496 //////////////////// TEST 0:
497 /* Create an approval signature field that we can sign after certifying.
498 (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
499 try {
500 const doc = await PDFNet.PDFDoc.createFromFilePath(input_path + 'waiver.pdf');
501 doc.initSecurityHandler();
502 const approval_signature_field = await doc.createDigitalSignatureField('PDFTronApprovalSig');
503 const widgetAnnotApproval = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(300, 287, 376, 306), approval_signature_field);
504 const page1 = await doc.getPage(1);
505 await page1.annotPushBack(widgetAnnotApproval);
506 await doc.save(output_path + 'waiver_withApprovalField_output.pdf', PDFNet.SDFDoc.SaveOptions.e_remove_unused);
507 } catch (err) {
508 console.log(err);
509 ret = 1;
510 }
511
512 //////////////////// TEST 1: certify a PDF.
513 try {
514 await CertifyPDF(input_path + 'waiver_withApprovalField.pdf',
515 'PDFTronCertificationSig',
516 input_path + 'pdftron.pfx',
517 'password',
518 input_path + 'pdftron.bmp',
519 output_path + 'waiver_withApprovalField_certified_output.pdf');
520 await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_output.pdf');
521 } catch (err) {
522 console.log(err);
523 ret = 1;
524 }
525
526 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
527 try {
528 await SignPDF(input_path + 'waiver_withApprovalField_certified.pdf',
529 'PDFTronApprovalSig',
530 input_path + 'pdftron.pfx',
531 'password',
532 input_path + 'signature.jpg',
533 output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
534 await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
535 } catch (err) {
536 console.log(err);
537 ret = 1;
538 }
539
540 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
541 try {
542 await ClearSignature(input_path + 'waiver_withApprovalField_certified_approved.pdf',
543 'PDFTronCertificationSig',
544 output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
545 await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
546 } catch (err) {
547 console.log(err);
548 ret = 1;
549 }
550
551 //////////////////// TEST 4: Verify a document's digital signatures.
552 // EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support or https://apryse.com/form/request
553 try {
554 if (!(await VerifyAllAndPrint(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'))) {
555 ret = 1;
556 }
557 } catch (err) {
558 console.log(err);
559 ret = 1;
560 }
561
562 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
563 try {
564 if (!(await VerifySimple(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'))) {
565 ret = 1;
566 }
567 } catch (err) {
568 console.log(err);
569 ret = 1;
570 }
571
572 //////////////////// TEST 6: Custom signing API.
573 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
574 // which allows users to customize the process of signing documents. Among other things, this
575 // includes the capability to allow for easy integration of PDF-specific signing-related operations
576 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
577 // to system keystores, etc.
578 try {
579 await CustomSigningAPI(input_path + 'waiver.pdf',
580 'PDFTronCertificationSig',
581 input_path + 'pdftron.pfx',
582 'password',
583 input_path + 'pdftron.cer',
584 input_path + 'signature.jpg',
585 PDFNet.DigestAlgorithm.Type.e_SHA256,
586 true,
587 output_path + 'waiver_custom_signed.pdf');
588 } catch (err) {
589 console.log(err);
590 ret = 1;
591 }
592
593 //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
594 // try {
595 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
596 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
597 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
598 // const tsa_url = 'YOUR_URL_OF_TSA';
599 // if (tsa_url == 'YOUR_URL_OF_TSA')
600 // {
601 // throw new Error('The URL of your timestamp authority was not specified.');
602 // }
603 //
604 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
605 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
606 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
607 // const trusted_cert_path = 'YOUR_CERTIFICATE';
608 // if (trusted_cert_path == 'YOUR_CERTIFICATE')
609 // {
610 // throw new Error('The path to your timestamp authority trusted root certificate was not specified.');
611 // }
612 //
613 // if (!(await TimestampAndEnableLTV(input_path + 'waiver.pdf',
614 // tsa_url,
615 // trusted_cert_path,
616 // input_path + 'signature.jpg',
617 // output_path + 'waiver_DocTimeStamp_LTV.pdf'))) {
618 // ret = 1;
619 // }
620 // } catch (err) {
621 // console.log(err);
622 // ret = 1;
623 // }
624
625 //////////////////// End of tests. ////////////////////
626 if (!ret) {
627 console.log('Tests successful.\n==========');
628 }
629 else {
630 console.log('Tests FAILED!!!\n==========');
631 }
632 };
633
634 PDFNet.runWithCleanup(main, PDFTronLicense.Key).catch(function (error) {
635 console.log('Error: ' + JSON.stringify(error));
636 }).then(function () { return PDFNet.shutdown(); });
637 };
638 exports.runDigitalSignatureTest();
639})(exports);
640// eslint-disable-next-line spaced-comment
641//# sourceURL=DigitalSignatureTest.js
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__'
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()
1<?php
2//----------------------------------------------------------------------------------------------------------------------
3// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
4// Consult LICENSE.txt regarding license information.
5//----------------------------------------------------------------------------------------------------------------------
6if(file_exists("../../../PDFNetC/Lib/PDFNetPHP.php"))
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
54include('../../../PDFNetC/Lib/PDFNetPHP.php');
55include("../../LicenseKey/PHP/LicenseKey.php");
56
57function VerifySimple($in_docpath, $in_public_key_file_path)
58{
59 $doc = new PDFDoc($in_docpath);
60 echo(nl2br("==========".PHP_EOL));
61 $opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);
62
63 // Add trust root to store of trusted certificates contained in VerificationOptions.
64 $opts->AddTrustedCertificate($in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);
65
66 $result = $doc->VerifySignedDigitalSignatures($opts);
67 switch ($result)
68 {
69 case PDFDoc::e_unsigned:
70 echo(nl2br("Document has no signed signature fields.".PHP_EOL));
71 return False;
72 /* e_failure == bad doc status, digest status, or permissions status
73 (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
74 case PDFDoc::e_failure:
75 echo(nl2br("Hard failure in verification on at least one signature.".PHP_EOL));
76 return False;
77 case PDFDoc::e_untrusted:
78 echo(nl2br("Could not verify trust for at least one signature.".PHP_EOL));
79 return False;
80 case PDFDoc::e_unsupported:
81 /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
82 unsupported features were encountered (requires verification using 'detailed' APIs) */
83 echo(nl2br("At least one signature contains unsupported features.".PHP_EOL));
84 return False;
85 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
86 case PDFDoc::e_verified:
87 echo(nl2br("All signed signatures in document verified.".PHP_EOL));
88 return True;
89 default:
90 echo(nl2br("unrecognized document verification status".PHP_EOL));
91 assert(False);
92 }
93}
94
95function VerifyAllAndPrint($in_docpath, $in_public_key_file_path)
96{
97 $doc = new PDFDoc($in_docpath);
98 echo(nl2br("==========".PHP_EOL));
99 $opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);
100
101 // Trust the public certificate we use for signing.
102 $trusted_cert_file = new MappedFile($in_public_key_file_path);
103 $file_sz = $trusted_cert_file->FileSize();
104 $file_reader = new FilterReader($trusted_cert_file);
105 $trusted_cert_buf = $file_reader->Read($file_sz);
106 $opts->AddTrustedCertificate($trusted_cert_buf, strlen($trusted_cert_buf), VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);
107
108 // Iterate over the signatures and verify all of them.
109 $digsig_fitr = $doc->GetDigitalSignatureFieldIterator();
110 $verification_status = True;
111 while ($digsig_fitr->HasNext())
112 {
113 $curr = $digsig_fitr->Current();
114 $result = $curr->Verify($opts);
115 if ($result->GetVerificationStatus())
116 {
117 echo(nl2br("Signature verified, objnum: ".strval($curr->GetSDFObj()->GetObjNum()).PHP_EOL));
118 }
119 else
120 {
121 echo(nl2br("Signature verification failed, objnum: ".strval($curr->GetSDFObj()->GetObjNum()).PHP_EOL));
122 $verification_status = False;
123 }
124
125 switch($result->GetDigestAlgorithm())
126 {
127 case DigestAlgorithm::e_SHA1:
128 echo(nl2br("Digest algorithm: SHA-1".PHP_EOL));
129 break;
130 case DigestAlgorithm::e_SHA256:
131 echo(nl2br("Digest algorithm: SHA-256".PHP_EOL));
132 break;
133 case DigestAlgorithm::e_SHA384:
134 echo(nl2br("Digest algorithm: SHA-384".PHP_EOL));
135 break;
136 case DigestAlgorithm::e_SHA512:
137 echo(nl2br("Digest algorithm: SHA-512".PHP_EOL));
138 break;
139 case DigestAlgorithm::e_RIPEMD160:
140 echo(nl2br("Digest algorithm: RIPEMD-160".PHP_EOL));
141 break;
142 case DigestAlgorithm::e_unknown_digest_algorithm:
143 echo(nl2br("Digest algorithm: unknown".PHP_EOL));
144 break;
145 default:
146 echo(nl2br("unrecognized digest algorithm".PHP_EOL));
147 assert(False);
148 }
149 echo(nl2br("Detailed verification result: \n\t".$result->GetDocumentStatusAsString()."\n\t"
150 .$result->GetDigestStatusAsString()."\n\t"
151 .$result->GetTrustStatusAsString()."\n\t"
152 .$result->GetPermissionsStatusAsString().PHP_EOL));
153
154
155 $changes = $result->GetDisallowedChanges();
156 for ($i = 0; $i < $changes->size(); $i++)
157 {
158 $change = $changes->get($i);
159 echo(nl2br("\tDisallowed change: ".strval($change->GetTypeAsString()).", objnum: ".strval($change->GetObjNum()).PHP_EOL));
160 }
161
162 // Get and print all the detailed trust-related results, if they are available.
163 if ($result->HasTrustVerificationResult())
164 {
165 $trust_verification_result = $result->GetTrustVerificationResult();
166 if ($trust_verification_result->WasSuccessful())
167 {
168 echo(nl2br("Trust verified.".PHP_EOL));
169 }
170 else
171 {
172 echo(nl2br("Trust not verifiable.".PHP_EOL));
173 }
174 echo(nl2br($trust_verification_result->GetResultString().PHP_EOL));
175
176 $tmp_time_t = $trust_verification_result->GetTimeOfTrustVerification();
177
178 switch ($trust_verification_result->GetTimeOfTrustVerificationEnum())
179 {
180 case VerificationOptions::e_current:
181 echo(nl2br("Trust verification attempted with respect to current time (as epoch time): ".$tmp_time_t.PHP_EOL));
182 break;
183 case VerificationOptions::e_signing:
184 echo(nl2br("Trust verification attempted with respect to signing time (as epoch time): ".$tmp_time_t.PHP_EOL));
185 break;
186 case VerificationOptions::e_timestamp:
187 echo(nl2br("Trust verification attempted with respect to secure embedded timestamp (as epoch time): ".$tmp_time_t.PHP_EOL));
188 break;
189 default:
190 echo(nl2br('unrecognized time enum value'.PHP_EOL));
191 assert(False);
192 }
193
194 if ($trust_verification_result->GetCertPath()->Size() == 0)
195 {
196 echo(nl2br("Could not print certificate path.\n"));
197 }
198 else
199 {
200 echo(nl2br("Certificate path:\n"));
201 $cert_path = $trust_verification_result->GetCertPath();
202 for ($j = 0; $j < $cert_path->Size(); $j++)
203 {
204 echo(nl2br("\tCertificate:\n"));
205 $full_cert = $cert_path->Get($j);
206 echo(nl2br("\t\tIssuer names:\n"));
207
208 $issuer_dn = $full_cert->GetIssuerField()->GetAllAttributesAndValues();
209 for ($i = 0; $i < $issuer_dn->Size(); $i++)
210 {
211 echo(nl2br("\t\t\t". $issuer_dn->Get($i)->GetStringValue()."\n"));
212 }
213 echo(nl2br("\t\tSubject names:\n"));
214 $subject_dn = $full_cert->GetSubjectField()->GetAllAttributesAndValues();
215 for ($i = 0; $i < $subject_dn->Size(); $i++)
216 {
217 echo(nl2br("\t\t\t".$subject_dn->Get($i)->GetStringValue()."\n"));
218 }
219 echo(nl2br("\t\tExtensions:\n"));
220 $ex = $full_cert->GetExtensions();
221 for ($i = 0; $i < $ex->Size(); $i++)
222 {
223 echo(nl2br("\t\t\t".$ex->Get($i)->ToString()."\n"));
224 }
225 }
226 }
227 }
228 else
229 {
230 echo(nl2br("No detailed trust verification result available."));
231 }
232
233 $unsupported_features = $result->GetUnsupportedFeatures();
234 if (count($unsupported_features) > 0)
235 {
236 echo(nl2br("Unsupported features:\n"));
237 for ($i = 0; $i < count($unsupported_features); $i++)
238 {
239 echo(nl2br("\t".$unsupported_features[$i]."\n"));
240 }
241 }
242 echo(nl2br("==========".PHP_EOL));
243
244 $digsig_fitr->Next();
245 }
246
247 return $verification_status;
248}
249
250function CertifyPDF($in_docpath,
251 $in_cert_field_name,
252 $in_private_key_file_path,
253 $in_keyfile_password,
254 $in_appearance_image_path,
255 $in_outpath)
256{
257
258 echo(nl2br('================================================================================'.PHP_EOL));
259 echo(nl2br('Certifying PDF document'.PHP_EOL));
260
261 // Open an existing PDF
262 $doc = new PDFDoc($in_docpath);
263
264 if ($doc->HasSignatures())
265 {
266 echo(nl2br('PDFDoc has signatures'.PHP_EOL));
267 }
268 else
269 {
270 echo(nl2br('PDFDoc has no signatures'.PHP_EOL));
271 }
272
273 $page1 = $doc->GetPage(1);
274
275 // Create a text field that we can lock using the field permissions feature.
276 $annot1 = TextWidget::Create($doc, new Rect(143.0, 440.0, 350.0, 460.0), "asdf_test_field");
277 $page1->AnnotPushBack($annot1);
278
279 // Create a new signature form field in the PDFDoc. The name argument is optional;
280 // leaving it empty causes it to be auto-generated. However, you may need the name for later.
281 // Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
282 // Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
283 $certification_sig_field = $doc->CreateDigitalSignatureField($in_cert_field_name);
284 $widgetAnnot = SignatureWidget::Create($doc, new Rect(143.0, 287.0, 219.0, 306.0), $certification_sig_field);
285 $page1->AnnotPushBack($widgetAnnot);
286
287 // (OPTIONAL) Add an appearance to the signature field.
288 $img = Image::Create($doc->GetSDFDoc(), $in_appearance_image_path);
289 $widgetAnnot->CreateSignatureAppearance($img);
290
291 // Prepare the document locking permission level. It will be applied upon document certification.
292 echo(nl2br('Adding document permissions.'.PHP_EOL));
293 $certification_sig_field->SetDocumentPermissions(DigitalSignatureField::e_annotating_formfilling_signing_allowed);
294
295 // Prepare to lock the text field that we created earlier.
296 echo(nl2br('Adding field permissions.'.PHP_EOL));
297 $certification_sig_field->SetFieldPermissions(DigitalSignatureField::e_include, array('asdf_test_field'));
298
299 $certification_sig_field->CertifyOnNextSave($in_private_key_file_path, $in_keyfile_password);
300
301 // (OPTIONAL) Add more information to the signature dictionary.
302 $certification_sig_field->SetLocation('Vancouver, BC');
303 $certification_sig_field->SetReason('Document certification.');
304 $certification_sig_field->SetContactInfo('www.pdftron.com');
305
306 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
307 $doc->Save($in_outpath, 0);
308
309 echo(nl2br('================================================================================'.PHP_EOL));
310}
311
312function SignPDF($in_docpath,
313 $in_approval_field_name,
314 $in_private_key_file_path,
315 $in_keyfile_password,
316 $in_appearance_img_path,
317 $in_outpath)
318{
319 echo(nl2br('================================================================================'.PHP_EOL));
320 echo(nl2br('Signing PDF document'.PHP_EOL));
321
322 // Open an existing PDF
323 $doc = new PDFDoc($in_docpath);
324
325 // Retrieve the unsigned approval signature field.
326 $found_approval_field = $doc->GetField($in_approval_field_name);
327 $found_approval_signature_digsig_field = new DigitalSignatureField($found_approval_field);
328
329 // (OPTIONAL) Add an appearance to the signature field.
330 $img = Image::Create($doc->GetSDFDoc(), $in_appearance_img_path);
331 $found_approval_signature_widget = new SignatureWidget($found_approval_field->GetSDFObj());
332 $found_approval_signature_widget->CreateSignatureAppearance($img);
333
334 // Prepare the signature and signature handler for signing.
335 $found_approval_signature_digsig_field->SignOnNextSave($in_private_key_file_path, $in_keyfile_password);
336
337 // The actual approval signing will be done during the following incremental save operation.
338 $doc->Save($in_outpath, SDFDoc::e_incremental);
339
340 echo(nl2br('================================================================================'.PHP_EOL));
341}
342
343function ClearSignature($in_docpath,
344 $in_digsig_field_name,
345 $in_outpath)
346{
347 echo(nl2br('================================================================================'.PHP_EOL));
348 echo(nl2br('Clearing certification signature'.PHP_EOL));
349
350 $doc = new PDFDoc($in_docpath);
351
352 $digsig = new DigitalSignatureField($doc->GetField($in_digsig_field_name));
353
354 echo(nl2br('Clearing signature: '.$in_digsig_field_name.PHP_EOL));
355 $digsig->ClearSignature();
356
357 if (!$digsig->HasCryptographicSignature())
358 {
359 echo(nl2br('Cryptographic signature cleared properly.'.PHP_EOL));
360 }
361
362 // Save incrementally so as to not invalidate other signatures from previous saves.
363 $doc->Save($in_outpath, SDFDoc::e_incremental);
364
365 echo(nl2br('================================================================================'.PHP_EOL));
366}
367
368function PrintSignaturesInfo($in_docpath)
369{
370 echo(nl2br('================================================================================'.PHP_EOL));
371 echo(nl2br('Reading and printing digital signature information'.PHP_EOL));
372
373 $doc = new PDFDoc($in_docpath);
374 if (!$doc->HasSignatures())
375 {
376 echo(nl2br('Doc has no signatures.'.PHP_EOL));
377 echo(nl2br('================================================================================'.PHP_EOL));
378 return;
379 }
380 else
381 {
382 echo(nl2br('Doc has signatures.'.PHP_EOL));
383 }
384
385 $fitr = $doc->GetFieldIterator();
386 while ($fitr->HasNext())
387 {
388 $current = $fitr->Current();
389 if ($current->IsLockedByDigitalSignature())
390 {
391 echo(nl2br("==========\nField locked by a digital signature".PHP_EOL));
392 }
393 else
394 {
395 echo(nl2br("==========\nField not locked by a digital signature".PHP_EOL));
396 }
397
398 echo(nl2br('Field name: '.$current->GetName().PHP_EOL));
399 echo(nl2br('=========='.PHP_EOL));
400
401 $fitr->Next();
402 }
403
404 echo(nl2br("====================\nNow iterating over digital signatures only.\n====================".PHP_EOL));
405
406 $digsig_fitr = $doc->GetDigitalSignatureFieldIterator();
407 while ($digsig_fitr->HasNext())
408 {
409 $current = $digsig_fitr->Current();
410 echo(nl2br('=========='.PHP_EOL));
411 $fld = new Field($current->GetSDFObj());
412 $fname = $fld->GetName();
413 echo(nl2br('Field name of digital signature: '.$fname.PHP_EOL));
414
415 $digsigfield = $current;
416 if (!$digsigfield->HasCryptographicSignature())
417 {
418 echo(nl2br("Either digital signature field lacks a digital signature dictionary, ".
419 "or digital signature dictionary lacks a cryptographic Contents entry. ".
420 "Digital signature field is not presently considered signed.\n".
421 "==========".PHP_EOL));
422 $digsig_fitr->Next();
423 continue;
424 }
425
426 $cert_count = $digsigfield->GetCertCount();
427 echo(nl2br('Cert count: '.strval($cert_count).PHP_EOL));
428 for ($i = 0; $i<$cert_count; ++$i)
429 {
430 $cert = $digsigfield->GetCert(i);
431 echo(nl2br('Cert #'.i.' size: '.$cert.length.PHP_EOL));
432 }
433
434 $subfilter = $digsigfield->GetSubFilter();
435
436 echo(nl2br('Subfilter type: '.strval($subfilter).PHP_EOL));
437
438 if ($subfilter !== DigitalSignatureField::e_ETSI_RFC3161)
439 {
440 echo(nl2br('Signature\'s signer: '.$digsigfield->GetSignatureName().PHP_EOL));
441
442 $signing_time = $digsigfield->GetSigningTime();
443 if ($signing_time->IsValid())
444 {
445 echo(nl2br('Signing time is valid.'.PHP_EOL));
446 }
447
448 echo(nl2br('Location: '.$digsigfield->GetLocation().PHP_EOL));
449 echo(nl2br('Reason: '.$digsigfield->GetReason().PHP_EOL));
450 echo(nl2br('Contact info: '.$digsigfield->GetContactInfo().PHP_EOL));
451 }
452 else
453 {
454 echo(nl2br('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)'.PHP_EOL));
455 }
456
457 if ($digsigfield->HasVisibleAppearance())
458 {
459 echo(nl2br('Visible'.PHP_EOL));
460 }
461 else
462 {
463 echo(nl2br('Not visible'.PHP_EOL));
464 }
465
466 $digsig_doc_perms = $digsigfield->GetDocumentPermissions();
467 $locked_fields = $digsigfield->GetLockedFields();
468 foreach ($locked_fields as $locked_field)
469 {
470 echo(nl2br('This digital signature locks a field named: '.$locked_field.PHP_EOL));
471 }
472
473 switch ($digsig_doc_perms)
474 {
475 case DigitalSignatureField::e_no_changes_allowed:
476 echo(nl2br('No changes to the document can be made without invalidating this digital signature.'.PHP_EOL));
477 break;
478 case DigitalSignatureField::e_formfilling_signing_allowed:
479 echo(nl2br('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.'.PHP_EOL));
480 break;
481 case DigitalSignatureField::e_annotating_formfilling_signing_allowed:
482 echo(nl2br('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.'.PHP_EOL));
483 break;
484 case DigitalSignatureField::e_unrestricted:
485 echo(nl2br('Document not restricted by this digital signature.'.PHP_EOL));
486 break;
487 default:
488 echo(nl2br('Unrecognized digital signature document permission level.'.PHP_EOL));
489 assert(false);
490 }
491
492 echo(nl2br('=========='.PHP_EOL));
493 $digsig_fitr->Next();
494 }
495
496 echo(nl2br('================================================================================'.PHP_EOL));
497}
498
499function CustomSigningAPI($doc_path,
500 $cert_field_name,
501 $private_key_file_path,
502 $keyfile_password,
503 $public_key_file_path,
504 $appearance_image_path,
505 $digest_algorithm_type,
506 $PAdES_signing_mode,
507 $output_path)
508{
509 echo(nl2br('================================================================================'.PHP_EOL));
510 echo(nl2br('Custom signing PDF document'.PHP_EOL));
511
512 $doc = new PDFDoc($doc_path);
513 $page1 = $doc->GetPage(1);
514
515 $digsig_field = $doc->CreateDigitalSignatureField($cert_field_name);
516 $widgetAnnot = SignatureWidget::Create($doc, new Rect(143.0, 287.0, 219.0, 306.0), $digsig_field);
517 $page1->AnnotPushBack($widgetAnnot);
518
519 // (OPTIONAL) Add an appearance to the signature field.
520 $img = Image::Create($doc->GetSDFDoc(), $appearance_image_path);
521 $widgetAnnot->CreateSignatureAppearance($img);
522
523 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
524 $digsig_field->CreateSigDictForCustomSigning("Adobe.PPKLite",
525 $PAdES_signing_mode ? DigitalSignatureField::e_ETSI_CAdES_detached : DigitalSignatureField::e_adbe_pkcs7_detached,
526 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.
527 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
528
529 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
530 $current_date = new Date();
531 $current_date->SetCurrentTime();
532 $digsig_field->SetSigDictTimeOfSigning($current_date);
533
534 $doc->Save($output_path, SDFDoc::e_incremental);
535
536 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
537 $pdf_digest = $digsig_field->CalculateDigest($digest_algorithm_type);
538
539 $signer_cert = new X509Certificate($public_key_file_path);
540
541 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
542 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
543 $pades_versioned_ess_signing_cert_attribute = DigitalSignatureField::GenerateESSSigningCertPAdESAttribute($signer_cert, $digest_algorithm_type);
544
545 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
546 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
547 $signedAttrs = DigitalSignatureField::GenerateCMSSignedAttributes($pdf_digest, $pades_versioned_ess_signing_cert_attribute);
548
549 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
550 $signedAttrs_digest = DigestAlgorithm::CalculateDigest($digest_algorithm_type, $signedAttrs);
551
552 //////////////////////////// custom digest signing starts ////////////////////////////
553 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
554 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
555 $signature_value = DigestAlgorithm::SignDigest(
556 $signedAttrs_digest,
557 $digest_algorithm_type,
558 $private_key_file_path,
559 $keyfile_password);
560 //////////////////////////// custom digest signing ends //////////////////////////////
561
562 // Then, load all your chain certificates into a container of X509Certificate.
563 $chain_certs = new VectorX509Certificate();
564
565 // Then, create ObjectIdentifiers for the algorithms you have used.
566 // Here we use digest_algorithm_type (SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
567 $digest_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_SHA256);
568 $signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_RSA_encryption_PKCS1);
569
570 // Then, put the CMS signature components together.
571 $cms_signature = DigitalSignatureField::GenerateCMSSignature(
572 $signer_cert, $chain_certs, $digest_algorithm_oid, $signature_algorithm_oid,
573 $signature_value, $signedAttrs);
574
575 // Write the signature to the document.
576 $doc->SaveCustomSignature($cms_signature, $digsig_field, $output_path);
577
578 echo(nl2br('================================================================================'.PHP_EOL));
579}
580
581function TimestampAndEnableLTV($in_docpath,
582 $in_tsa_url,
583 $in_trusted_cert_path,
584 $in_appearance_img_path,
585 $in_outpath)
586{
587 $doc = new PDFDoc($in_docpath);
588 $doctimestamp_signature_field = $doc->CreateDigitalSignatureField();
589 $tst_config = new TimestampingConfiguration($in_tsa_url);
590 $opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);
591 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
592 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
593 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
594 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
595 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
596 $opts->AddTrustedCertificate($in_trusted_cert_path);
597 /* By default, we only check online for revocation of certificates using the newer and lighter
598 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
599 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
600 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
601 $opts->EnableOnlineCRLRevocationChecking(true);
602
603 $widgetAnnot = SignatureWidget::Create($doc, new Rect(0.0, 100.0, 200.0, 150.0), $doctimestamp_signature_field);
604 $doc->GetPage(1)->AnnotPushBack($widgetAnnot);
605
606 // (OPTIONAL) Add an appearance to the signature field.
607 $img = Image::Create($doc->GetSDFDoc(), $in_appearance_img_path);
608 $widgetAnnot->CreateSignatureAppearance($img);
609
610 echo(nl2br('Testing timestamping configuration.'.PHP_EOL));
611 $config_result = $tst_config->TestConfiguration($opts);
612 if ($config_result->GetStatus())
613 {
614 echo(nl2br('Success: timestamping configuration usable. Attempting to timestamp.'.PHP_EOL));
615 }
616 else
617 {
618 // Print details of timestamping failure.
619 echo(nl2br($config_result->GetString().PHP_EOL));
620 if ($config_result->HasResponseVerificationResult())
621 {
622 $tst_result = $config_result->GetResponseVerificationResult();
623 echo(nl2br('CMS digest status: '.$tst_result->GetCMSDigestStatusAsString().PHP_EOL));
624 echo(nl2br('Message digest status: '.$tst_result->GetMessageImprintDigestStatusAsString().PHP_EOL));
625 echo(nl2br('Trust status: '.$tst_result->GetTrustStatusAsString().PHP_EOL));
626 }
627 return false;
628 }
629
630 $doctimestamp_signature_field->TimestampOnNextSave($tst_config, $opts);
631
632 // Save/signing throws if timestamping fails.
633 $doc->Save($in_outpath, SDFDoc::e_incremental);
634
635 echo(nl2br('Timestamping successful. Adding LTV information for DocTimeStamp signature.'.PHP_EOL));
636
637 // Add LTV information for timestamp signature to document.
638 $timestamp_verification_result = $doctimestamp_signature_field->Verify($opts);
639 if (!$doctimestamp_signature_field->EnableLTVOfflineVerification($timestamp_verification_result))
640 {
641 echo(nl2br('Could not enable LTV for DocTimeStamp.'.PHP_EOL));
642 return false;
643 }
644 $doc->Save($in_outpath, SDFDoc::e_incremental);
645 echo(nl2br('Added LTV information for DocTimeStamp signature successfully.'.PHP_EOL));
646
647 return true;
648}
649
650function main()
651{
652 global $LicenseKey;
653 // Initialize PDFNet
654 PDFNet::Initialize($LicenseKey);
655
656 $result = true;
657 $input_path = '../../TestFiles/';
658 $output_path = '../../TestFiles/Output/';
659
660 //////////////////// TEST 0:
661 // Create an approval signature field that we can sign after certifying.
662 // (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
663 // Open an existing PDF
664 try
665 {
666 $doc = new PDFDoc($input_path.'waiver.pdf');
667 $widgetAnnotApproval = SignatureWidget::Create($doc, new Rect(300.0, 287.0, 376.0, 306.0), 'PDFTronApprovalSig');
668 $page1 = $doc->GetPage(1);
669 $page1->AnnotPushBack($widgetAnnotApproval);
670 $doc->Save($output_path.'waiver_withApprovalField_output.pdf', SDFDoc::e_remove_unused);
671 }
672 catch (Exception $e)
673 {
674 echo(nl2br($e->getMessage().PHP_EOL));
675 echo(nl2br($e->getTraceAsString().PHP_EOL));
676 $result = false;
677 }
678 //////////////////// TEST 1: certify a PDF.
679 try
680 {
681 CertifyPDF($input_path.'waiver_withApprovalField.pdf',
682 'PDFTronCertificationSig',
683 $input_path.'pdftron.pfx',
684 'password',
685 $input_path.'pdftron.bmp',
686 $output_path.'waiver_withApprovalField_certified_output.pdf');
687 PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_output.pdf');
688 }
689 catch (Exception $e)
690 {
691 echo(nl2br($e->getMessage().PHP_EOL));
692 echo(nl2br($e->getTraceAsString().PHP_EOL));
693 $result = false;
694 }
695 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
696 try
697 {
698 SignPDF($input_path.'waiver_withApprovalField_certified.pdf',
699 'PDFTronApprovalSig',
700 $input_path.'pdftron.pfx',
701 'password',
702 $input_path.'signature.jpg',
703 $output_path.'waiver_withApprovalField_certified_approved_output.pdf');
704 PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_approved_output.pdf');
705 }
706 catch (Exception $e)
707 {
708 echo(nl2br($e->getMessage().PHP_EOL));
709 echo(nl2br($e->getTraceAsString().PHP_EOL));
710 $result = false;
711 }
712 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
713 try
714 {
715 ClearSignature($input_path.'waiver_withApprovalField_certified_approved.pdf',
716 'PDFTronCertificationSig',
717 $output_path.'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
718 PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
719 }
720 catch (Exception $e)
721 {
722 echo(nl2br($e->getMessage().PHP_EOL));
723 echo(nl2br($e->getTraceAsString().PHP_EOL));
724 $result = false;
725 }
726 //////////////////// TEST 4: Verify a document's digital signatures.
727 try
728 {
729 if (!VerifyAllAndPrint($input_path.'waiver_withApprovalField_certified_approved.pdf', $input_path.'pdftron.cer'))
730 {
731 $result = false;
732 }
733 }
734 catch (Exception $e)
735 {
736 echo(nl2br($e->getMessage().PHP_EOL));
737 echo(nl2br($e->getTraceAsString().PHP_EOL));
738 $result = false;
739 }
740 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
741 try
742 {
743 if (!VerifySimple($input_path.'waiver_withApprovalField_certified_approved.pdf', $input_path.'pdftron.cer'))
744 {
745 $result = false;
746 }
747 }
748 catch (Exception $e)
749 {
750 echo(nl2br($e->getMessage().PHP_EOL));
751 echo(nl2br($e->getTraceAsString().PHP_EOL));
752 $result = false;
753 }
754
755 //////////////////// TEST 6: Custom signing API.
756 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
757 // which allows users to customize the process of signing documents. Among other things, this
758 // includes the capability to allow for easy integration of PDF-specific signing-related operations
759 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
760 // to system keystores, etc.
761 try
762 {
763 CustomSigningAPI($input_path.'waiver.pdf',
764 'PDFTronApprovalSig',
765 $input_path.'pdftron.pfx',
766 'password',
767 $input_path.'pdftron.cer',
768 $input_path.'signature.jpg',
769 DigestAlgorithm::e_SHA256,
770 true,
771 $output_path.'waiver_custom_signed.pdf');
772 }
773 catch (Exception $e)
774 {
775 echo(nl2br($e->getMessage().PHP_EOL));
776 echo(nl2br($e->getTraceAsString().PHP_EOL));
777 $result = false;
778 }
779
780 //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
781 // try
782 // {
783 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
784 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
785 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
786 // $tsa_url = 'YOUR_URL_OF_TSA';
787 // if ($tsa_url == 'YOUR_URL_OF_TSA')
788 // {
789 // throw new Exception('Error: The URL of your timestamp authority was not specified.');
790 // }
791 //
792 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
793 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
794 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
795 // $trusted_cert_path = 'YOUR_CERTIFICATE';
796 // if ($trusted_cert_path == 'YOUR_CERTIFICATE')
797 // {
798 // throw new Exception('Error: The path to your timestamp authority trusted root certificate was not specified.');
799 // }
800 //
801 // if(!TimestampAndEnableLTV($input_path.'waiver.pdf',
802 // $tsa_url,
803 // $trusted_cert_path,
804 // $input_path.'signature.jpg',
805 // $output_path.'waiver_DocTimeStamp_LTV.pdf'))
806 // {
807 // $result = false;
808 // }
809 // }
810 // catch (Exception $e)
811 // {
812 // echo(nl2br($e->getMessage().PHP_EOL));
813 // echo(nl2br($e->getTraceAsString().PHP_EOL));
814 // $result = false;
815 // }
816
817 //////////////////// End of tests. ////////////////////
818 PDFNet::Terminate();
819 if (!$result)
820 {
821 echo(nl2br("Tests FAILED!!!\n==========".PHP_EOL));
822 return;
823 }
824
825 echo(nl2br("Tests successful.\n==========".PHP_EOL));
826}
827
828main();
829
830?>
1'
2' Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3'
4
5''----------------------------------------------------------------------------------------------------------------------
6'' This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
7''
8'' The following steps reflect typical intended usage of the digital signatures API:
9''
10'' 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)).
11''
12'' 1. EITHER:
13'' (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
14'' -OR-
15'' (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
16'' document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
17''
18'' 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
19'' If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
20'' NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
21''
22'' [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
23'' or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
24'' certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
25'' is signed.]
26''
27'' 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
28'' a. Taking a PKCS #12 keyfile path and its password
29'' b. Taking a buffer containing a PKCS #12 private keyfile and its password
30'' c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
31'' in the following fashion:
32'' i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
33'' validate/check a digital signature.
34'' ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
35'' pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
36'' iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
37'' NOTE: It is only possible to sign/certify one signature per call to the Save function.
38''
39'' 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
40'' IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
41'' so as to not invalidate the other signature(s).
42''
43'' Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
44'' of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
45'' add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
46''
47'' Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
48'' StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
49''----------------------------------------------------------------------------------------------------------------------
50
51'' In order to use .NET Framework's Cryptography library, define "USE_DOTNET_CRYPTO" and then add System.Security to
52'' references list.
53
54Imports System
55Imports System.Collections.Generic
56Imports System.IO
57#If USE_DOTNET_CRYPTO Then
58Imports System.Security.Cryptography
59Imports System.Security.Cryptography.Pkcs
60Imports System.Security.Cryptography.X509Certificates
61#End If ' USE_DOTNET_CRYPTO
62
63Imports pdftron
64Imports pdftron.Crypto
65Imports pdftron.PDF
66Imports pdftron.PDF.Annots
67Imports pdftron.SDF
68
69'''''''''''''''''''' Here follows an example of how to implement a custom signature handler. ''''''''''
70#If USE_DOTNET_CRYPTO Then
71Class DotNetCryptoSignatureHandler
72 Inherits SignatureHandler
73 Private m_data As List(Of Byte)
74 Private m_signingCert As String
75 Private m_certPassword As String
76
77 Public Sub New(ByVal signingCert As String, ByVal password As String)
78 m_signingCert = signingCert
79 m_certPassword = password
80 m_data = New List(Of Byte)()
81 End Sub
82
83 Public Overrides Sub AppendData(ByVal data As Byte())
84 m_data.AddRange(data)
85 End Sub
86
87 Public Overrides Function Reset() As Boolean
88 m_data.Clear()
89 Return (True)
90 End Function
91
92 Public Overrides Function CreateSignature() As Byte()
93 Try
94 Dim ci As New ContentInfo(m_data.ToArray())
95 Dim sc As New SignedCms(ci, True)
96 Dim cert As New X509Certificate2(m_signingCert, m_certPassword)
97 Dim cs As New CmsSigner()
98 cs.Certificate = cert
99 cs.DigestAlgorithm = New Oid("2.16.840.1.101.3.4.2.1") ' SHA-256
100 sc.ComputeSignature(cs)
101 Dim sig As Byte() = sc.Encode()
102 Return (sig)
103 Catch e As Exception
104 Console.[Error].WriteLine(e)
105 End Try
106 Return (Nothing)
107 End Function
108
109 Public Overrides Function GetName() As String
110 Return ("Adobe.PPKLite")
111 End Function
112
113 Protected Overrides Sub Finalize()
114 Try
115 Console.Out.WriteLine("DotNetCryptoSignatureHandler Destructor.")
116 Finally
117 MyBase.Finalize()
118 End Try
119 End Sub
120End Class
121#End If ' USE_DOTNET_CRYPTO
122'''''''''' End of the DotNetCryptoSignatureHandler custom handler code. ''''''''''''''''''''
123
124Module Module1
125 Dim input_path As String = "../../../../TestFiles/"
126 Dim output_path As String = "../../../../TestFiles/Output/"
127
128 Public Function VerifySimple(ByVal in_docpath As String, ByVal in_public_key_file_path As String) As Boolean
129 Using doc As PDFDoc = New PDFDoc(in_docpath)
130 Console.WriteLine("==========")
131 Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)
132
133 'Add trust root to store of trusted certificates contained in VerificationOptions.
134 opts.AddTrustedCertificate(in_public_key_file_path, System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_default_trust) Or System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_certification_trust))
135
136 Dim result As PDFDoc.SignaturesVerificationStatus = doc.VerifySignedDigitalSignatures(opts)
137 Select Case result
138
139 Case PDFDoc.SignaturesVerificationStatus.e_unsigned
140 Console.WriteLine("Document has no signed signature fields.")
141 Return False
142 ' e_failure == bad doc status, digest status, or permissions status
143 ' (i.e. does not include trust issues, because those are flaky due to being network/config-related)
144 Case PDFDoc.SignaturesVerificationStatus.e_failure
145 Console.WriteLine("Hard failure in verification on at least one signature.")
146 Return False
147 Case PDFDoc.SignaturesVerificationStatus.e_untrusted
148 Console.WriteLine("Could not verify trust for at least one signature.")
149 Return False
150 Case PDFDoc.SignaturesVerificationStatus.e_unsupported
151 'If necessary, call GetUnsupportedFeatures on VerificationResult to check which
152 ' unsupported features were encountered (requires verification using 'detailed' APIs)
153 Console.WriteLine("At least one signature contains unsupported features.")
154 Return False
155 'unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
156 Case PDFDoc.SignaturesVerificationStatus.e_verified
157 Console.WriteLine("All signed signatures in document verified.")
158 Return True
159 Case Else
160 Throw New Exception("unrecognized document verification status")
161 End Select
162 End Using
163 End Function
164
165 Public Function VerifyAllAndPrint(ByVal in_docpath As String, ByVal in_public_key_file_path As String) As Boolean
166 Using doc As PDFDoc = New PDFDoc(in_docpath)
167 Console.WriteLine("==========")
168 Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)
169
170 ' Trust the public certificate we use for signing.
171 Dim trusted_cert_buf As Byte() = File.ReadAllBytes(in_public_key_file_path)
172 opts.AddTrustedCertificate(trusted_cert_buf, System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_default_trust) Or System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_certification_trust))
173
174 ' Iterate over the signatures and verify all of them.
175 Dim digsig_fitr As DigitalSignatureFieldIterator = doc.GetDigitalSignatureFieldIterator()
176 Dim verification_status As Boolean = True
177 While digsig_fitr.HasNext()
178 Dim curr As DigitalSignatureField = digsig_fitr.Current()
179 Dim result As VerificationResult = curr.Verify(opts)
180
181 If result.GetVerificationStatus() Then
182 Console.Write("Signature verified, ")
183 Else
184 Console.Write("Signature verification failed, ")
185 verification_status = False
186 End If
187
188 Console.WriteLine("objnum: {0}", curr.GetSDFObj().GetObjNum())
189
190 Select Case result.GetDigestAlgorithm()
191 Case DigestAlgorithm.Type.e_sha1:
192 Console.WriteLine("Digest algorithm: SHA-1")
193 Case DigestAlgorithm.Type.e_sha256:
194 Console.WriteLine("Digest algorithm: SHA-256")
195 Case DigestAlgorithm.Type.e_sha384:
196 Console.WriteLine("Digest algorithm: SHA-384")
197 Case DigestAlgorithm.Type.e_sha512:
198 Console.WriteLine("Digest algorithm: SHA-512")
199 Case DigestAlgorithm.Type.e_ripemd160:
200 Console.WriteLine("Digest algorithm: RIPEMD-160")
201 Case DigestAlgorithm.Type.e_unknown_digest_algorithm:
202 Console.WriteLine("Digest algorithm: unknown")
203 Case Else
204 Throw New Exception("unrecognized digest algorithm")
205 End Select
206
207 Console.WriteLine("Detailed verification result: " & vbLf & vbTab & "{0}" & vbLf & vbTab & "{1}" & vbLf & vbTab & "{2}" & vbLf & vbTab & "{3}", result.GetDocumentStatusAsString(), result.GetDigestStatusAsString(), result.GetTrustStatusAsString(), result.GetPermissionsStatusAsString())
208
209 Dim changes As DisallowedChange() = result.GetDisallowedChanges()
210
211 For Each it2 As DisallowedChange In changes
212 Console.WriteLine(vbTab & "Disallowed change: {0}, objnum: {1}", it2.GetTypeAsString(), it2.GetObjNum())
213 Next
214
215 ' Get and print all the detailed trust-related results, if they are available.
216 If result.HasTrustVerificationResult() Then
217 Dim trust_verification_result As TrustVerificationResult = result.GetTrustVerificationResult()
218 Console.WriteLine(If(trust_verification_result.WasSuccessful(), "Trust verified.", "Trust not verifiable."))
219 Console.WriteLine(trust_verification_result.GetResultString())
220 Dim time_of_verification As ULong = trust_verification_result.GetTimeOfTrustVerification()
221
222 Select Case trust_verification_result.GetTimeOfTrustVerificationEnum()
223 Case VerificationOptions.TimeMode.e_current
224 Console.WriteLine("Trust verification attempted with respect to current time (as epoch time): {0}", time_of_verification)
225 Case VerificationOptions.TimeMode.e_signing
226 Console.WriteLine("Trust verification attempted with respect to signing time (as epoch time): {0}", time_of_verification)
227 Case VerificationOptions.TimeMode.e_timestamp
228 Console.WriteLine("Trust verification attempted with respect to secure embedded timestamp (as epoch time): {0}", time_of_verification)
229 Case Else
230 Throw New Exception("unrecognized time enum value")
231 End Select
232
233 If trust_verification_result.GetCertPath().Length = 0 Then
234 Console.WriteLine("Could not print certificate path.")
235 Else
236 Console.WriteLine("Certificate path:")
237 Dim cert_path As X509Certificate() = trust_verification_result.GetCertPath()
238
239 For j As Integer = 0 To cert_path.Length - 1
240 Console.WriteLine(vbTab & "Certificate:")
241 Dim full_cert As X509Certificate = cert_path(j)
242 Console.WriteLine(vbTab & vbTab & "Issuer names:")
243 Dim issuer_dn As X501AttributeTypeAndValue() = full_cert.GetIssuerField().GetAllAttributesAndValues()
244
245 For i As Integer = 0 To issuer_dn.Length - 1
246 Console.WriteLine(vbTab & vbTab & vbTab & issuer_dn(i).GetStringValue())
247 Next
248
249 Console.WriteLine(vbTab & vbTab & "Subject names:")
250 Dim subject_dn As X501AttributeTypeAndValue() = full_cert.GetSubjectField().GetAllAttributesAndValues()
251
252 For i As Integer = 0 To subject_dn.Length - 1
253 Console.WriteLine(vbTab & vbTab & vbTab & subject_dn(i).GetStringValue())
254 Next
255
256 Console.WriteLine(vbTab & vbTab & "Extensions:")
257
258 For i As Integer = 0 To full_cert.GetExtensions().Length - 1
259 Console.WriteLine(vbTab & vbTab & vbTab & full_cert.GetExtensions()(i).ToString())
260 Next
261 Next
262 End If
263 Else
264 Console.WriteLine("No detailed trust verification result available.")
265 End If
266
267 Dim unsupported_features As String() = result.GetUnsupportedFeatures()
268
269 If unsupported_features.Length > 0 Then
270 Console.WriteLine("Unsupported features:")
271
272 For i As Integer = 0 To unsupported_features.Length - 1
273 Console.WriteLine(vbTab & unsupported_features(i))
274 Next
275 End If
276
277 Console.WriteLine("==========")
278 digsig_fitr.[Next]()
279 End While
280
281 Return verification_status
282 End Using
283 End Function
284
285
286
287 Sub CertifyPDF(ByVal in_docpath As String, ByVal in_cert_field_name As String, ByVal in_private_key_file_path As String, ByVal in_keyfile_password As String, ByVal in_appearance_image_path As String, ByVal in_outpath As String)
288 Console.Out.WriteLine("================================================================================")
289 Console.Out.WriteLine("Certifying PDF document")
290
291 ' Open an existing PDF
292 Using doc As PDFDoc = New PDFDoc(in_docpath)
293 Console.Out.WriteLine("PDFDoc has " & (If(doc.HasSignatures(), "signatures", "no signatures")))
294
295 Dim page1 As Page = doc.GetPage(1)
296
297 ' Create a text field that we can lock using the field permissions feature.
298 Dim annot1 As TextWidget = TextWidget.Create(doc, New Rect(143, 440, 350, 460), "asdf_test_field")
299 page1.AnnotPushBack(annot1)
300
301 ' Create a new signature form field in the PDFDoc. The name argument is optional;
302 ' leaving it empty causes it to be auto-generated. However, you may need the name for later.
303 ' Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
304 ' Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
305 Dim certification_sig_field As DigitalSignatureField = doc.CreateDigitalSignatureField(in_cert_field_name)
306 Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(143, 287, 219, 306), certification_sig_field)
307 page1.AnnotPushBack(widgetAnnot)
308
309 ' (OPTIONAL) Add an appearance to the signature field.
310 Dim img As Image = Image.Create(doc, in_appearance_image_path)
311 widgetAnnot.CreateSignatureAppearance(img)
312
313 ' Add permissions. Lock the random text field.
314 Console.Out.WriteLine("Adding document permissions.")
315 certification_sig_field.SetDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed)
316
317 ' Prepare to lock the text field that we created earlier.
318 Console.Out.WriteLine("Adding field permissions.")
319 Dim fields_to_lock As String() = New String(0) {}
320 fields_to_lock(0) = "asdf_test_field"
321 certification_sig_field.SetFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock)
322
323#If USE_DOTNET_CRYPTO Then
324 Dim sigHandler As DotNetCryptoSignatureHandler = New DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password)
325 Dim sigHandlerId As SignatureHandlerId = doc.AddSignatureHandler(sigHandler)
326 certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId)
327 ' Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format
328 ' for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format,
329 ' so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
330 ' when using the StdSignatureHandler.
331 Dim f_obj As Obj = certification_sig_field.GetSDFObj()
332 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached")
333#Else
334 certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password)
335#End If ' USE_DOTNET_CRYPTO
336
337 ' (OPTIONAL) Add more information to the signature dictionary.
338 certification_sig_field.SetLocation("Vancouver, BC")
339 certification_sig_field.SetReason("Document certification.")
340 certification_sig_field.SetContactInfo("www.pdftron.com")
341
342 ' Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
343 doc.Save(in_outpath, 0)
344 End Using
345
346 Console.Out.WriteLine("================================================================================")
347 End Sub
348
349 Sub SignPDF(ByVal in_docpath As String, ByVal in_approval_field_name As String, ByVal in_private_key_file_path As String, ByVal in_keyfile_password As String, ByVal in_appearance_img_path As String, ByVal in_outpath As String)
350 Console.Out.WriteLine("================================================================================")
351 Console.Out.WriteLine("Signing PDF document")
352
353 ' Open an existing PDF
354 Using doc As PDFDoc = New PDFDoc(in_docpath)
355 ' Retrieve the unsigned approval signature field.
356 Dim found_approval_field As Field = doc.GetField(in_approval_field_name)
357 Dim found_approval_signature_digsig_field As DigitalSignatureField = New DigitalSignatureField(found_approval_field)
358
359 ' (OPTIONAL) Add an appearance to the signature field.
360 Dim img As Image = Image.Create(doc, in_appearance_img_path)
361 Dim found_approval_signature_widget As SignatureWidget = New SignatureWidget(found_approval_field.GetSDFObj())
362 found_approval_signature_widget.CreateSignatureAppearance(img)
363
364 ' Prepare the signature and signature handler for signing.
365#If USE_DOTNET_CRYPTO Then
366 Dim sigHandler As DotNetCryptoSignatureHandler = New DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password)
367 Dim sigHandlerId As SignatureHandlerId = doc.AddSignatureHandler(sigHandler)
368 found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId)
369#Else
370 found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password)
371 ' Add a SubFilter name that uniquely identifies the signature format for verification tools. As an
372 ' example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, so we embed
373 ' one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this
374 ' when using the StdSignatureHandler.
375 Dim f_obj As Obj = found_approval_signature_digsig_field.GetSDFObj()
376 f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached")
377#End If ' USE_DOTNET_CRYPTO
378
379 ' The actual approval signing will be done during the following incremental save operation.
380 doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental)
381 End Using
382
383 Console.Out.WriteLine("================================================================================")
384 End Sub
385
386 Sub ClearSignature(ByVal in_docpath As String, ByVal in_digsig_field_name As String, ByVal in_outpath As String)
387 Console.Out.WriteLine("================================================================================")
388 Console.Out.WriteLine("Clearing certification signature")
389
390 Using doc As PDFDoc = New PDFDoc(in_docpath)
391 Dim digsig As DigitalSignatureField = New DigitalSignatureField(doc.GetField(in_digsig_field_name))
392
393 Console.Out.WriteLine("Clearing signature: " & in_digsig_field_name)
394 digsig.ClearSignature()
395
396 If Not digsig.HasCryptographicSignature() Then
397 Console.Out.WriteLine("Cryptographic signature cleared properly.")
398 End If
399
400 ' Save incrementally so as to not invalidate other signatures from previous saves.
401 doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental)
402 End Using
403
404 Console.Out.WriteLine("================================================================================")
405 End Sub
406
407 Sub PrintSignaturesInfo(ByVal in_docpath As String)
408 Console.Out.WriteLine("================================================================================")
409 Console.Out.WriteLine("Reading and printing digital signature information")
410
411 Using doc As PDFDoc = New PDFDoc(in_docpath)
412
413 If Not doc.HasSignatures() Then
414 Console.Out.WriteLine("Doc has no signatures.")
415 Console.Out.WriteLine("================================================================================")
416 Return
417 Else
418 Console.Out.WriteLine("Doc has signatures.")
419 End If
420
421 Dim fitr As FieldIterator = doc.GetFieldIterator()
422
423 While fitr.HasNext()
424 If fitr.Current().IsLockedByDigitalSignature() Then
425 Console.Out.WriteLine("==========" & vbLf & "Field locked by a digital signature")
426 Else
427 Console.Out.WriteLine("==========" & vbLf & "Field not locked by a digital signature")
428 End If
429
430 Console.Out.WriteLine("Field name: " & fitr.Current().GetName())
431 Console.Out.WriteLine("==========")
432 fitr.Next()
433 End While
434
435 Console.Out.WriteLine("====================" & vbLf & "Now iterating over digital signatures only." & vbLf & "====================")
436
437 Dim digsig_fitr As DigitalSignatureFieldIterator = doc.GetDigitalSignatureFieldIterator()
438 While digsig_fitr.HasNext()
439 Console.Out.WriteLine("==========")
440 Console.Out.WriteLine("Field name of digital signature: " & New Field(digsig_fitr.Current().GetSDFObj()).GetName())
441 Dim digsigfield As DigitalSignatureField = digsig_fitr.Current()
442
443 If Not digsigfield.HasCryptographicSignature() Then
444 Console.Out.WriteLine("Either digital signature field lacks a digital signature dictionary, " &
445 "or digital signature dictionary lacks a cryptographic Contents entry. " &
446 "Digital signature field is not presently considered signed." &
447 vbLf & "==========")
448 digsig_fitr.Next()
449 Continue While
450 End If
451
452 Dim cert_count As Integer = digsigfield.GetCertCount()
453 Console.Out.WriteLine("Cert count: " & cert_count)
454 For i As Integer = 0 To cert_count - 1
455 Dim cert As Byte() = digsigfield.GetCert(i)
456 Console.Out.WriteLine("Cert #" & i & " size: " & cert.Length)
457 Next
458
459 Dim subfilter As DigitalSignatureField.SubFilterType = digsigfield.GetSubFilter()
460 Console.Out.WriteLine("Subfilter type: " & CInt(subfilter))
461
462 If subfilter <> DigitalSignatureField.SubFilterType.e_ETSI_RFC3161 Then
463 Console.Out.WriteLine("Signature's signer: " & digsigfield.GetSignatureName())
464 Dim signing_time As pdftron.PDF.Date = digsigfield.GetSigningTime()
465 If signing_time.IsValid() Then
466 Console.Out.WriteLine("Signing time is valid.")
467 End If
468 Console.Out.WriteLine("Location: " & digsigfield.GetLocation())
469 Console.Out.WriteLine("Reason: " & digsigfield.GetReason())
470 Console.Out.WriteLine("Contact info: " & digsigfield.GetContactInfo())
471 Else
472 Console.Out.WriteLine("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)" & vbLf)
473 End If
474
475 Console.Out.WriteLine((If((digsigfield.HasVisibleAppearance()), "Visible", "Not visible")))
476
477 Dim digsig_doc_perms As DigitalSignatureField.DocumentPermissions = digsigfield.GetDocumentPermissions()
478 Dim locked_fields As String() = digsigfield.GetLockedFields()
479 For Each field_name As String In locked_fields
480 Console.Out.WriteLine("This digital signature locks a field named: " & field_name)
481 Next
482
483 Select Case digsig_doc_perms
484 Case DigitalSignatureField.DocumentPermissions.e_no_changes_allowed
485 Console.Out.WriteLine("No changes to the document can be made without invalidating this digital signature.")
486 Case DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed
487 Console.Out.WriteLine("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
488 Case DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed
489 Console.Out.WriteLine("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
490 Case DigitalSignatureField.DocumentPermissions.e_unrestricted
491 Console.Out.WriteLine("Document not restricted by this digital signature.")
492 Case Else
493 Throw New Exception("Unrecognized digital signature document permission level.")
494 End Select
495
496 Console.Out.WriteLine("==========")
497 digsig_fitr.Next()
498 End While
499 End Using
500
501 Console.Out.WriteLine("================================================================================")
502 End Sub
503
504 Sub CustomSigningAPI(ByVal doc_path As String, ByVal cert_field_name As String, ByVal private_key_file_path As String, ByVal keyfile_password As String, ByVal public_key_file_path As String, ByVal appearance_image_path As String, ByVal digest_algorithm_type As DigestAlgorithm.Type, ByVal PAdES_signing_mode As Boolean, ByVal output_path As String)
505 Console.Out.WriteLine("================================================================================")
506 Console.Out.WriteLine("Custom signing PDF document")
507
508 Using doc As PDFDoc = New PDFDoc(doc_path)
509 Dim page1 As Page = doc.GetPage(1)
510
511 Dim digsig_field As DigitalSignatureField = doc.CreateDigitalSignatureField(cert_field_name)
512 Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(143, 287, 219, 306), digsig_field)
513 page1.AnnotPushBack(widgetAnnot)
514
515 ' (OPTIONAL) Add an appearance to the signature field.
516 Dim img As Image = Image.Create(doc, appearance_image_path)
517 widgetAnnot.CreateSignatureAppearance(img)
518
519 ' Create a digital signature dictionary inside the digital signature field, in preparation for signing.
520 digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
521 If(PAdES_signing_mode, DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached, DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached),
522 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.
523 ' ... Or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
524
525 ' (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support Is available from your signing provider.
526 Dim current_date As pdftron.PDF.Date = New pdftron.PDF.Date()
527 current_date.SetCurrentTime()
528 digsig_field.SetSigDictTimeOfSigning(current_date)
529
530 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)
531
532 ' Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
533 Dim pdf_digest As Byte() = digsig_field.CalculateDigest(digest_algorithm_type)
534
535 Dim signer_cert As X509Certificate = New X509Certificate(public_key_file_path)
536
537 ' Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
538 ' The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
539 Dim pades_versioned_ess_signing_cert_attribute As Byte() = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)
540
541 ' Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
542 ' The signedAttrs are certain attributes that become protected by their inclusion in the signature.
543 Dim signedAttrs As Byte() = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)
544
545 ' Calculate the digest of the signedAttrs (i.e. Not the PDF digest, this time).
546 Dim signedAttrs_digest As Byte() = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs)
547
548 '''''''''''''''''''''''''''' custom digest signing starts '''''''''''''''''''''''''''''
549 ' At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
550 ' which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
551 Dim signature_value As Byte() = DigestAlgorithm.SignDigest(signedAttrs_digest, digest_algorithm_type, private_key_file_path, keyfile_password)
552 '''''''''''''''''''''''''''' custom digest signing ends '''''''''''''''''''''''''''''''
553
554 ' Then, load all your chain certificates into a container of X509Certificate.
555 Dim chain_certs() As X509Certificate = {}
556
557 ' Then, create ObjectIdentifiers for the algorithms you have used.
558 ' Here we use digest_algorithm_type (usually SHA256) for hashing, And RSAES-PKCS1-v1_5 (specified in the private key) for signing.
559 Dim digest_algorithm_oid As ObjectIdentifier = New ObjectIdentifier(digest_algorithm_type)
560 Dim signature_algorithm_oid As ObjectIdentifier = New ObjectIdentifier(ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1)
561
562 ' Then, put the CMS signature components together.
563 Dim cms_signature As Byte() = DigitalSignatureField.GenerateCMSSignature(signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid, signature_value, signedAttrs)
564
565 ' Write the signature to the document.
566 doc.SaveCustomSignature(cms_signature, digsig_field, output_path)
567 End Using
568
569 Console.Out.WriteLine("================================================================================")
570 End Sub
571
572 Public Function TimestampAndEnableLTV(ByVal doc_path As String, ByVal tsa_url As String, ByVal trusted_cert_path As String, ByVal appearance_img_path As String, ByVal output_path As String) As Boolean
573 Using doc As PDFDoc = New PDFDoc(doc_path)
574 Dim doctimestamp_signature_field As DigitalSignatureField = doc.CreateDigitalSignatureField()
575 Dim tst_config As TimestampingConfiguration = New TimestampingConfiguration(tsa_url)
576 Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)
577 opts.AddTrustedCertificate(trusted_cert_path)
578 opts.EnableOnlineCRLRevocationChecking(True)
579 Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(0, 100, 200, 150), doctimestamp_signature_field)
580 doc.GetPage(1).AnnotPushBack(widgetAnnot)
581 Dim widgetObj As Obj = widgetAnnot.GetSDFObj()
582 Dim img As Image = Image.Create(doc, appearance_img_path)
583 widgetAnnot.CreateSignatureAppearance(img)
584 Console.WriteLine("Testing timestamping configuration.")
585 Dim config_result As TimestampingResult = tst_config.TestConfiguration(opts)
586
587 If config_result.GetStatus() Then
588 Console.WriteLine("Success: timestamping configuration usable. Attempting to timestamp.")
589 Else
590 Console.WriteLine(config_result.GetString())
591
592 If config_result.HasResponseVerificationResult() Then
593 Dim tst_result As EmbeddedTimestampVerificationResult = config_result.GetResponseVerificationResult()
594 Console.WriteLine("CMS digest status: {0}" & vbLf, tst_result.GetCMSDigestStatusAsString())
595 Console.WriteLine("Message digest status: {0}" & vbLf, tst_result.GetMessageImprintDigestStatusAsString())
596 Console.WriteLine("Trust status: {0}" & vbLf, tst_result.GetTrustStatusAsString())
597 End If
598
599 Return False
600 End If
601
602 doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts)
603 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)
604 Console.WriteLine("Timestamping successful. Adding LTV information for DocTimeStamp signature.")
605 Dim timestamp_verification_result As VerificationResult = doctimestamp_signature_field.Verify(opts)
606
607 If Not doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result) Then
608 Console.WriteLine("Could not enable LTV for DocTimeStamp.")
609 Return False
610 End If
611
612 doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)
613 Console.WriteLine("Added LTV information for DocTimeStamp signature successfully.")
614 Return True
615 End Using
616 End Function
617
618 Dim pdfNetLoader As PDFNetLoader
619 Sub New()
620 pdfNetLoader = pdftron.PDFNetLoader.Instance()
621 End Sub
622
623 Sub Main()
624 ' Initialize PDFNet
625 PDFNet.Initialize(PDFTronLicense.Key)
626
627 Dim result As Boolean = True
628
629 '''''''''''''''''''' TEST 0:
630 ' Create an approval signature field that we can sign after certifying.
631 ' (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
632 Try
633 Using doc As PDFDoc = New PDFDoc(input_path & "waiver.pdf")
634 Dim approval_signature_field As DigitalSignatureField = doc.CreateDigitalSignatureField("PDFTronApprovalSig")
635 Dim widgetAnnotApproval As SignatureWidget = SignatureWidget.Create(doc, New Rect(300, 287, 376, 306), approval_signature_field)
636 Dim page1 As Page = doc.GetPage(1)
637 page1.AnnotPushBack(widgetAnnotApproval)
638 doc.Save(output_path & "waiver_withApprovalField_output.pdf", SDFDoc.SaveOptions.e_remove_unused)
639 End Using
640 Catch e As Exception
641 Console.[Error].WriteLine(e)
642 result = False
643 End Try
644
645 '''''''''''''''''''' TEST 1: certify a PDF.
646 Try
647 CertifyPDF(input_path & "waiver_withApprovalField.pdf", "PDFTronCertificationSig", input_path & "pdftron.pfx", "password", input_path & "pdftron.bmp", output_path & "waiver_withApprovalField_certified_output.pdf")
648 PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_output.pdf")
649 Catch e As Exception
650 Console.[Error].WriteLine(e)
651 result = False
652 End Try
653
654 '''''''''''''''''''' TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
655 Try
656 SignPDF(input_path & "waiver_withApprovalField_certified.pdf", "PDFTronApprovalSig", input_path & "pdftron.pfx", "password", input_path & "signature.jpg", output_path & "waiver_withApprovalField_certified_approved_output.pdf")
657 PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_approved_output.pdf")
658 Catch e As Exception
659 Console.[Error].WriteLine(e)
660 result = False
661 End Try
662
663 '''''''''''''''''''' TEST 3: Clear a certification from a document that is certified and has an approval signature.
664 Try
665 ClearSignature(input_path & "waiver_withApprovalField_certified_approved.pdf", "PDFTronCertificationSig", output_path & "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
666 PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
667 Catch e As Exception
668 Console.[Error].WriteLine(e)
669 result = False
670 End Try
671
672 '''''''''''''''''''' TEST 4: Verify a document's digital signatures.
673 Try
674 If Not VerifyAllAndPrint(input_path & "waiver_withApprovalField_certified_approved.pdf", input_path & "pdftron.cer") Then
675 result = False
676 End If
677 Catch e As Exception
678 Console.[Error].WriteLine(e)
679 result = False
680 End Try
681
682 '''''''''''''''''''' TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
683 Try
684 If Not VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer") Then
685 result = False
686 End If
687 Catch e As Exception
688 Console.[Error].WriteLine(e)
689 result = False
690 End Try
691
692 '''''''''''''''''''' TEST 6 Custom signing API.
693 ' The Apryse custom signing API Is a set of APIs related to cryptographic digital signatures
694 ' which allows users to customize the process of signing documents. Among other things, this
695 ' includes the capability to allow for easy integration of PDF-specific signing-related operations
696 ' with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
697 ' to system keystores, etc.
698 Try
699 CustomSigningAPI(input_path & "waiver.pdf",
700 "PDFTronApprovalSig",
701 input_path & "pdftron.pfx",
702 "password",
703 input_path & "pdftron.cer",
704 input_path & "signature.jpg",
705 DigestAlgorithm.Type.e_sha256,
706 True,
707 output_path & "waiver_custom_signed.pdf")
708 Catch e As Exception
709 Console.[Error].WriteLine(e)
710 result = False
711 End Try
712
713 '''''''''''''''''''' TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
714 ' Try
715 ' ' Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
716 ' ' For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
717 ' ' Note that this url may not work in the future. A reliable solution requires using your own TSA.
718 ' Dim tsa_url As String = "YOUR_URL_OF_TSA"
719 ' If String.Compare(tsa_url, "YOUR_URL_OF_TSA") = 0 Then
720 ' Throw New Exception("Error: The URL of your timestamp authority was not specified.")
721 ' End If
722 '
723 ' ' Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
724 ' ' For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
725 ' ' Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
726 ' Dim trusted_cert_path As String = "YOUR_CERTIFICATE"
727 ' If String.Compare(trusted_cert_path, "YOUR_CERTIFICATE") = 0 Then
728 ' Throw New Exception("Error: The path to your timestamp authority trusted root certificate was not specified.")
729 ' End If
730 '
731 ' If Not TimestampAndEnableLTV(input_path + "waiver.pdf",
732 ' tsa_url,
733 ' trusted_cert_path,
734 ' input_path + "signature.jpg",
735 ' output_path + "waiver_DocTimeStamp_LTV.pdf") Then
736 ' result = False
737 ' End If
738 '
739 ' Catch e As Exception
740 ' Console.[Error].WriteLine(e)
741 ' result = False
742 ' End Try
743
744 '''''''''''''''''''' End of tests. ''''''''''''''''''''
745 PDFNet.Terminate()
746 If result Then
747 Console.Out.WriteLine("Tests successful." & vbLf & "==========")
748 Else
749 Console.Out.WriteLine("Tests FAILED!!!" & vbLf & "==========")
750 End If
751 End Sub
752End Module
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales