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) {