DigitalSignatures

Sample Java code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files. Learn more about our Android SDK and PDF Digital Signature Library.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2019 by PDFTron Systems Inc. All Rights Reserved.
3// Consult legal.txt regarding legal and license information.
4//---------------------------------------------------------------------------------------
5
6package com.pdftron.android.pdfnetsdksamples.samples;
7
8import com.pdftron.android.pdfnetsdksamples.OutputListener;
9import com.pdftron.android.pdfnetsdksamples.PDFNetSample;
10import com.pdftron.android.pdfnetsdksamples.R;
11import com.pdftron.android.pdfnetsdksamples.util.Utils;
12import com.pdftron.common.PDFNetException;
13import com.pdftron.crypto.X501AttributeTypeAndValue;
14import com.pdftron.crypto.X509Certificate;
15import com.pdftron.pdf.Date;
16import com.pdftron.pdf.DigitalSignatureField;
17import com.pdftron.pdf.DigitalSignatureFieldIterator;
18import com.pdftron.pdf.DisallowedChange;
19import com.pdftron.pdf.EmbeddedTimestampVerificationResult;
20import com.pdftron.pdf.Field;
21import com.pdftron.pdf.FieldIterator;
22import com.pdftron.pdf.Image;
23import com.pdftron.pdf.PDFDoc;
24import com.pdftron.pdf.Page;
25import com.pdftron.pdf.Rect;
26import com.pdftron.pdf.TimestampingConfiguration;
27import com.pdftron.pdf.TimestampingResult;
28import com.pdftron.pdf.TrustVerificationResult;
29import com.pdftron.pdf.VerificationOptions;
30import com.pdftron.pdf.VerificationResult;
31import com.pdftron.pdf.annots.SignatureWidget;
32import com.pdftron.pdf.annots.TextWidget;
33import com.pdftron.sdf.SDFDoc;
34
35import java.util.ArrayList;
36import java.util.Locale;
37
38//----------------------------------------------------------------------------------------------------------------------
39// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
40//
41// The following steps reflect typical intended usage of the digital signatures API:
42//
43// 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)).
44//
45// 1. EITHER:
46// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
47// -OR-
48// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
49// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
50//
51// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
52// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
53// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
54//
55// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
56// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
57// certain types of changes to be made to the document without invalidating the cryptographic digital signature's hash once it
58// is signed.]
59//
60// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
61// a. Taking a PKCS #12 keyfile path and its password
62// b. Taking a buffer containing a PKCS #12 private keyfile and its password
63// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
64// in the following fashion:
65// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
66// validate/check a digital signature.
67// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
68// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
69// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
70// NOTE: It is only possible to sign/certify one signature per call to the Save function.
71//
72// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic hash to it.
73// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
74// so as to not invalidate the other signature's('s) cryptographic hashes.
75//
76// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
77// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
78// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
79//
80// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
81// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
82//----------------------------------------------------------------------------------------------------------------------
83
84public class DigitalSignaturesTest extends PDFNetSample {
85
86 private static OutputListener mOutputListener;
87
88 private static ArrayList<String> mFileList = new ArrayList<>();
89
90 public DigitalSignaturesTest() {
91 setTitle(R.string.sample_digitalsignatures_title);
92 setDescription(R.string.sample_digitalsignatures_description);
93
94 // After proper setup (eg. Spongy Castle libs installed,
95 // MySignatureHandler.createSignature() returns a valid buffer), please comment line below
96 // to enable sample.
97 // If you are using the full library, you do not need to use the custom signature handler.
98 // PDFDoc has a standard signature handler that can be used instead. Check the code below
99 // for more info.
100 //DisableRun();
101 }
102
103 @Override
104 public void run(OutputListener outputListener) {
105
106 mOutputListener = outputListener;
107 printHeader(outputListener);
108 printFooter(outputListener);
109 }
110
111 @Override
112 public void runOnBackground() {
113 super.runOnBackground();
114 mFileList.clear();
115 // Initialize PDFNet
116
117 boolean result = true;
118
119 //////////////////// TEST 0:
120 /* Create an approval signature field that we can sign after certifying.
121 (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
122 try (PDFDoc doc = new PDFDoc(Utils.getAssetTempFile(INPUT_PATH + "waiver.pdf").getAbsolutePath()))
123 {
124 DigitalSignatureField approval_signature_field = doc.createDigitalSignatureField("PDFTronApprovalSig");
125 SignatureWidget widgetAnnotApproval = SignatureWidget.create(doc, new Rect(300, 287, 376, 306), approval_signature_field);
126 Page page1 = doc.getPage(1);
127 page1.annotPushBack(widgetAnnotApproval);
128 doc.save(Utils.createExternalFile("waiver_withApprovalField_output.pdf", mFileList).getAbsolutePath(), SDFDoc.SaveMode.REMOVE_UNUSED, null);
129 }
130 catch (Exception e)
131 {
132 System.err.println(e.getMessage());
133 e.printStackTrace(System.err);
134 result = false;
135 }
136
137 //////////////////// TEST 1: certify a PDF.
138 try
139 {
140 certifyPDF(Utils.getAssetTempFile(INPUT_PATH + "waiver_withApprovalField.pdf").getAbsolutePath(),
141 "PDFTronCertificationSig",
142 Utils.getAssetTempFile(INPUT_PATH + "pdftron.pfx").getAbsolutePath(),
143 "password",
144 Utils.getAssetTempFile(INPUT_PATH + "pdftron.bmp").getAbsolutePath(),
145 Utils.createExternalFile("waiver_withApprovalField_certified_output.pdf", mFileList).getAbsolutePath());
146 printSignaturesInfo(Utils.createExternalFile("waiver_withApprovalField_certified_output.pdf", mFileList).getAbsolutePath());
147 }
148 catch (Exception e)
149 {
150 System.err.println(e.getMessage());
151 e.printStackTrace(System.err);
152 result = false;
153 }
154
155 //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
156 try
157 {
158 signPDF(Utils.getAssetTempFile(INPUT_PATH + "waiver_withApprovalField_certified.pdf").getAbsolutePath(),
159 "PDFTronApprovalSig",
160 Utils.getAssetTempFile(INPUT_PATH + "pdftron.pfx").getAbsolutePath(),
161 "password",
162 Utils.getAssetTempFile(INPUT_PATH + "signature.jpg").getAbsolutePath(),
163 Utils.createExternalFile("waiver_withApprovalField_certified_approved_output.pdf", mFileList).getAbsolutePath());
164 printSignaturesInfo(Utils.createExternalFile("waiver_withApprovalField_certified_approved_output.pdf", mFileList).getAbsolutePath());
165 }
166 catch (Exception e)
167 {
168 System.err.println(e.getMessage());
169 e.printStackTrace(System.err);
170 result = false;
171 }
172
173 //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
174 try
175 {
176 clearSignature(Utils.getAssetTempFile(INPUT_PATH + "waiver_withApprovalField_certified_approved.pdf").getAbsolutePath(),
177 "PDFTronCertificationSig",
178 Utils.createExternalFile("waiver_withApprovalField_certified_approved_certcleared_output.pdf", mFileList).getAbsolutePath());
179 printSignaturesInfo(Utils.createExternalFile("waiver_withApprovalField_certified_approved_certcleared_output.pdf", mFileList).getAbsolutePath());
180 }
181 catch (Exception e)
182 {
183 System.err.println(e.getMessage());
184 e.printStackTrace(System.err);
185 result = false;
186 }
187
188 //////////////////// TEST 4: Verify a document's digital signatures.
189 try
190 {
191 if (!verifyAllAndPrint(Utils.getAssetTempFile(INPUT_PATH + "waiver_withApprovalField_certified_approved.pdf").getAbsolutePath(),
192 Utils.getAssetTempFile(INPUT_PATH + "pdftron.cer").getAbsolutePath()))
193 {
194 result = false;
195 }
196 }
197 catch (Exception e)
198 {
199 System.err.println(e.getMessage());
200 e.printStackTrace(System.err);
201 result = false;
202 }
203 //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
204 try
205 {
206 if (!verifySimple(Utils.getAssetTempFile(INPUT_PATH + "waiver_withApprovalField_certified_approved.pdf").getAbsolutePath(),
207 Utils.getAssetTempFile(INPUT_PATH + "pdftron.cer").getAbsolutePath()))
208 {
209 result = false;
210 }
211 }
212 catch (Exception e)
213 {
214 System.err.println(e.getMessage());
215 e.printStackTrace(System.err);
216 result = false;
217
218 }
219 //////////////////// TEST 6: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
220 //try
221 //{
222 // if (!timestampAndEnableLTV(Utils.getAssetTempFile(INPUT_PATH + "waiver.pdf").getAbsolutePath(),
223 // Utils.getAssetTempFile(INPUT_PATH + "GlobalSignRootForTST.cer").getAbsolutePath(),
224 // Utils.getAssetTempFile(INPUT_PATH + "signature.jpg").getAbsolutePath(),
225 // Utils.createExternalFile("waiver_DocTimeStamp_LTV.pdf", mFileList).getAbsolutePath()))
226 // {
227 // result = false;
228 // }
229 //}
230 //catch (Exception e)
231 //{
232 // System.err.println(e.getMessage());
233 // e.printStackTrace(System.err);
234 // result = false;
235 //
236 //}
237
238
239 //////////////////// End of tests. ////////////////////
240
241 if (result)
242 {
243 System.out.println("Tests successful.\n==========");
244 }
245 else
246 {
247 System.out.println("Tests FAILED!!!\n==========");
248 }
249
250 for (String file : mFileList) {
251 addToFileList(file);
252 }
253 }
254 public static boolean verifySimple(String in_docpath, String in_public_key_file_path) throws PDFNetException
255 {
256 PDFDoc doc = new PDFDoc(in_docpath);
257 System.out.println("==========");
258 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
259
260 // Add trust root to store of trusted certificates contained in VerificationOptions.
261 opts.addTrustedCertificate(in_public_key_file_path,
262 VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);
263
264 PDFDoc.SignaturesVerificationStatus result = doc.verifySignedDigitalSignatures(opts);
265
266 switch (result)
267 {
268 case e_unsigned:
269 System.out.println("Document has no signed signature fields.");
270 return false;
271 /*e_failure == bad doc status, digest status, or permissions status
272 (i.e. does not include trust issues, because those are flaky due to being network/config-related)*/
273 case e_failure:
274 System.out.println("Hard failure in verification on at least one signature.");
275 return false;
276 case e_untrusted:
277 System.out.println("Could not verify trust for at least one signature.");
278 return false;
279 case e_unsupported:
280 /*If necessary, call GetUnsupportedFeatures on VerificationResult to check which
281 unsupported features were encountered (requires verification using 'detailed' APIs) */
282 System.out.println("At least one signature contains unsupported features.");
283 return false;
284 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
285 case e_verified:
286 System.out.println("All signed signatures in document verified.");
287 return true;
288 default:
289 System.err.println("unrecognized document verification status");
290 assert(false);
291 }
292 return false;
293 }
294
295 public static boolean verifyAllAndPrint(String in_docpath, String in_public_key_file_path) throws PDFNetException
296 {
297 PDFDoc doc = new PDFDoc(in_docpath);
298 System.out.println("==========");
299 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
300
301 // Add trust root to store of trusted certificates contained in VerificationOptions.
302 opts.addTrustedCertificate(in_public_key_file_path,
303 VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);
304
305 // Iterate over the signatures and verify all of them.
306 DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
307 boolean verification_status = true;
308 for (; digsig_fitr.hasNext(); )
309 {
310 DigitalSignatureField curr = digsig_fitr.next();
311 VerificationResult result = curr.verify(opts);
312 if (result.getVerificationStatus())
313 {
314 System.out.print("Signature verified, ");
315 }
316 else
317 {
318 System.out.print("Signature verification failed, ");
319 verification_status = false;
320 }
321 System.out.println(String.format(Locale.US, "objnum: %d", curr.getSDFObj().getObjNum()));
322
323 switch (result.getDigestAlgorithm())
324 {
325 case e_sha1:
326 System.out.println("Digest algorithm: SHA-1");
327 break;
328 case e_sha256:
329 System.out.println("Digest algorithm: SHA-256");
330 break;
331 case e_sha384:
332 System.out.println("Digest algorithm: SHA-384");
333 break;
334 case e_sha512:
335 System.out.println("Digest algorithm: SHA-512");
336 break;
337 case e_ripemd160:
338 System.out.println("Digest algorithm: RIPEMD-160");
339 break;
340 case e_unknown_digest_algorithm:
341 System.out.println("Digest algorithm: unknown");
342 break;
343 default:
344 System.err.println("unrecognized digest algorithm");
345 assert(false);
346 }
347 System.out.println(String.format("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s",
348 result.getDocumentStatusAsString(),
349 result.getDigestStatusAsString(),
350 result.getTrustStatusAsString(),
351 result.getPermissionsStatusAsString()));
352
353 DisallowedChange[] changes = result.getDisallowedChanges();
354 for (DisallowedChange it2 : changes)
355 {
356 System.out.println(String.format(Locale.US, "\tDisallowed change: %s, objnum: %d", it2.getTypeAsString(), it2.getObjNum()));
357 }
358
359 // Get and print all the detailed trust-related results, if they are available.
360 if (result.hasTrustVerificationResult())
361 {
362 TrustVerificationResult trust_verification_result = result.getTrustVerificationResult();
363 System.out.println(trust_verification_result.wasSuccessful() ? "Trust verified." : "Trust not verifiable.");
364 System.out.println(trust_verification_result.getResultString());
365
366 long time_of_verification = trust_verification_result.getTimeOfTrustVerification();
367 switch (trust_verification_result.getTimeOfTrustVerificationEnum())
368 {
369 case e_current:
370 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to current time (as epoch time): %d", time_of_verification));
371 break;
372 case e_signing:
373 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to signing time (as epoch time): %d", time_of_verification));
374 break;
375 case e_timestamp:
376 System.out.println(String.format(Locale.US, "Trust verification attempted with respect to secure embedded timestamp (as epoch time): %d", time_of_verification));
377 break;
378 default:
379 System.err.println("unrecognized time enum value");
380 assert(false);
381 }
382
383 if(trust_verification_result.getCertPath().length == 0 )
384 {
385 System.out.println("Could not print certificate path.");
386 }
387 else
388 {
389 System.out.println("Certificate path:");
390 X509Certificate[] cert_path = trust_verification_result.getCertPath();
391 for (int j = 0; j < cert_path.length; j++)
392 {
393 System.out.println("\tCertificate:");
394 X509Certificate full_cert = cert_path[j];
395 System.out.println("\t\tIssuer names:");
396
397 X501AttributeTypeAndValue[] issuer_dn = full_cert.getIssuerField().getAllAttributesAndValues();
398 for (int i = 0; i < issuer_dn.length; i++)
399 {
400 System.out.println("\t\t\t" + issuer_dn[i].getStringValue());
401 }
402 System.out.println("\t\tSubject names:");
403 X501AttributeTypeAndValue[] subject_dn = full_cert.getSubjectField().getAllAttributesAndValues();
404 for (int i = 0; i < subject_dn.length; i++)
405 {
406 System.out.println("\t\t\t" + subject_dn[i].getStringValue());
407 }
408 System.out.println("\t\tExtensions:");
409 for (int i = 0; i < full_cert.getExtensions().length; i++)
410 {
411 System.out.println("\t\t\t" + full_cert.getExtensions()[i].toString());
412 }
413 }
414 }
415 }
416 else
417 {
418 System.out.println("No detailed trust verification result available.");
419 }
420
421 String[] unsupported_features = result.getUnsupportedFeatures();
422 if (unsupported_features.length > 0)
423 {
424 System.out.println("Unsupported features:");
425
426 for (String unsupported_feature : unsupported_features)
427 {
428 System.out.println("\t" + unsupported_feature);
429 }
430 }
431 System.out.println("==========");
432 }
433
434 return verification_status;
435 }
436
437 public static void certifyPDF(String in_docpath,
438 String in_cert_field_name,
439 String in_private_key_file_path,
440 String in_keyfile_password,
441 String in_appearance_image_path,
442 String in_outpath) throws PDFNetException
443 {
444 System.out.println("================================================================================");
445 System.out.println("Certifying PDF document");
446
447 // Open an existing PDF
448 PDFDoc doc = new PDFDoc(in_docpath);
449
450 if (doc.hasSignatures())
451 {
452 System.out.println("PDFDoc has signatures");
453 }
454 else
455 {
456 System.out.println("PDFDoc has no signatures");
457 }
458
459 Page page1 = doc.getPage(1);
460
461 // Create a text field that we can lock using the field permissions feature.
462 TextWidget annot1 = TextWidget.create(doc, new Rect(143, 440, 350, 460), "asdf_test_field");
463 page1.annotPushBack(annot1);
464
465 /* Create a new signature form field in the PDFDoc. The name argument is optional;
466 leaving it empty causes it to be auto-generated. However, you may need the name for later.
467 Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
468 Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
469 DigitalSignatureField certification_sig_field = doc.createDigitalSignatureField(in_cert_field_name);
470 SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(143, 287, 219, 306), certification_sig_field);
471 page1.annotPushBack(widgetAnnot);
472
473 // (OPTIONAL) Add an appearance to the signature field.
474 Image img = Image.create(doc, in_appearance_image_path);
475 widgetAnnot.createSignatureAppearance(img);
476
477 // Prepare the document locking permission level. It will be applied upon document certification.
478 System.out.println("Adding document permissions.");
479 certification_sig_field.setDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
480
481 // Prepare to lock the text field that we created earlier.
482 System.out.println("Adding field permissions.");
483 String[] fields_to_lock = {"asdf_test_field"};
484 certification_sig_field.setFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);
485
486 certification_sig_field.certifyOnNextSave(in_private_key_file_path, in_keyfile_password);
487
488 // (OPTIONAL) Add more information to the signature dictionary.
489 certification_sig_field.setLocation("Vancouver, BC");
490 certification_sig_field.setReason("Document certification.");
491 certification_sig_field.setContactInfo("www.pdftron.com");
492
493 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
494 doc.save(in_outpath, SDFDoc.SaveMode.NO_FLAGS, null);
495
496 System.out.println("================================================================================");
497 }
498
499 public static void signPDF(String in_docpath,
500 String in_approval_field_name,
501 String in_private_key_file_path,
502 String in_keyfile_password,
503 String in_appearance_img_path,
504 String in_outpath) throws PDFNetException
505 {
506 System.out.println("================================================================================");
507 System.out.println("Signing PDF document");
508
509 // Open an existing PDF
510 PDFDoc doc = new PDFDoc(in_docpath);
511
512 // Retrieve the unsigned approval signature field.
513 Field found_approval_field = doc.getField(in_approval_field_name);
514 DigitalSignatureField found_approval_signature_digsig_field = new DigitalSignatureField(found_approval_field);
515
516 // (OPTIONAL) Add an appearance to the signature field.
517 Image img = Image.create(doc, in_appearance_img_path);
518 SignatureWidget found_approval_signature_widget = new SignatureWidget(found_approval_field.getSDFObj());
519 found_approval_signature_widget.createSignatureAppearance(img);
520
521 // Prepare the signature and signature handler for signing.
522 found_approval_signature_digsig_field.signOnNextSave(in_private_key_file_path, in_keyfile_password);
523
524 // The actual approval signing will be done during the following incremental save operation.
525 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
526
527 System.out.println("================================================================================");
528 }
529
530 public static void clearSignature(String in_docpath,
531 String in_digsig_field_name,
532 String in_outpath) throws PDFNetException
533 {
534 System.out.println("================================================================================");
535 System.out.println("Clearing certification signature");
536
537 PDFDoc doc = new PDFDoc(in_docpath);
538
539 DigitalSignatureField digsig = new DigitalSignatureField(doc.getField(in_digsig_field_name));
540
541 System.out.println("Clearing signature: " + in_digsig_field_name);
542 digsig.clearSignature();
543
544 if (!digsig.hasCryptographicSignature())
545 {
546 System.out.println("Cryptographic signature cleared properly.");
547 }
548
549 // Save incrementally so as to not invalidate other signatures from previous saves.
550 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
551
552 System.out.println("================================================================================");
553 }
554
555 public static void printSignaturesInfo(String in_docpath) throws PDFNetException
556 {
557 System.out.println("================================================================================");
558 System.out.println("Reading and printing digital signature information");
559
560 PDFDoc doc = new PDFDoc(in_docpath);
561 if (!doc.hasSignatures())
562 {
563 System.out.println("Doc has no signatures.");
564 System.out.println("================================================================================");
565 return;
566 }
567 else
568 {
569 System.out.println("Doc has signatures.");
570 }
571
572
573 for (FieldIterator fitr = doc.getFieldIterator(); fitr.hasNext(); )
574 {
575 Field current = fitr.next();
576 if (current.isLockedByDigitalSignature())
577 {
578 System.out.println("==========\nField locked by a digital signature");
579 }
580 else
581 {
582 System.out.println("==========\nField not locked by a digital signature");
583 }
584
585 System.out.println("Field name: " + current.getName());
586 System.out.println("==========");
587 }
588
589 System.out.println("====================\nNow iterating over digital signatures only.\n====================");
590
591 DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
592 for (; digsig_fitr.hasNext(); )
593 {
594 DigitalSignatureField current = digsig_fitr.next();
595 System.out.println("==========");
596 System.out.println("Field name of digital signature: " + new Field(current.getSDFObj()).getName());
597
598 DigitalSignatureField digsigfield = current;
599 if (!digsigfield.hasCryptographicSignature())
600 {
601 System.out.println("Either digital signature field lacks a digital signature dictionary, " +
602 "or digital signature dictionary lacks a cryptographic Contents entry. " +
603 "Digital signature field is not presently considered signed.\n" +
604 "==========");
605 continue;
606 }
607
608 int cert_count = digsigfield.getCertCount();
609 System.out.println("Cert count: " + cert_count);
610 for (int i = 0; i < cert_count; ++i)
611 {
612 byte[] cert = digsigfield.getCert(i);
613 System.out.println("Cert #" + i + " size: " + cert.length);
614 }
615
616 DigitalSignatureField.SubFilterType subfilter = digsigfield.getSubFilter();
617
618 System.out.println("Subfilter type: " + subfilter.ordinal());
619
620 if (subfilter != DigitalSignatureField.SubFilterType.e_ETSI_RFC3161)
621 {
622 System.out.println("Signature's signer: " + digsigfield.getSignatureName());
623
624 Date signing_time = digsigfield.getSigningTime();
625 if (signing_time.isValid())
626 {
627 System.out.println("Signing time is valid.");
628 }
629
630 System.out.println("Location: " + digsigfield.getLocation());
631 System.out.println("Reason: " + digsigfield.getReason());
632 System.out.println("Contact info: " + digsigfield.getContactInfo());
633 }
634 else
635 {
636 System.out.println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)");
637 }
638
639 if (digsigfield.hasVisibleAppearance())
640 {
641 System.out.println("Visible");
642 }
643 else
644 {
645 System.out.println("Not visible");
646 }
647
648 DigitalSignatureField.DocumentPermissions digsig_doc_perms = digsigfield.getDocumentPermissions();
649 String[] locked_fields = digsigfield.getLockedFields();
650 for (String it : locked_fields)
651 {
652 System.out.println("This digital signature locks a field named: " + it);
653 }
654
655 switch (digsig_doc_perms)
656 {
657 case e_no_changes_allowed:
658 System.out.println("No changes to the document can be made without invalidating this digital signature.");
659 break;
660 case e_formfilling_signing_allowed:
661 System.out.println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
662 break;
663 case e_annotating_formfilling_signing_allowed:
664 System.out.println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
665 break;
666 case e_unrestricted:
667 System.out.println("Document not restricted by this digital signature.");
668 break;
669 default:
670 System.err.println("Unrecognized digital signature document permission level.");
671 assert(false);
672 }
673 System.out.println("==========");
674 }
675
676 System.out.println("================================================================================");
677 }
678
679 public static boolean timestampAndEnableLTV(String in_docpath,
680 String in_trusted_cert_path,
681 String in_appearance_img_path,
682 String in_outpath) throws PDFNetException
683 {
684 PDFDoc doc = new PDFDoc(in_docpath);
685 DigitalSignatureField doctimestamp_signature_field = doc.createDigitalSignatureField();
686 TimestampingConfiguration tst_config = new TimestampingConfiguration("http://rfc3161timestamp.globalsign.com/advanced");
687 VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
688 /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
689 the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
690 response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
691 function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
692 in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
693 opts.addTrustedCertificate(in_trusted_cert_path);
694 /* By default, we only check online for revocation of certificates using the newer and lighter
695 OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
696 it may be necessary to enable online CRL revocation checking in order to verify some timestamps
697 (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
698 opts.enableOnlineCRLRevocationChecking(true);
699
700 SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(0, 100, 200, 150), doctimestamp_signature_field);
701 doc.getPage(1).annotPushBack(widgetAnnot);
702
703 // (OPTIONAL) Add an appearance to the signature field.
704 Image img = Image.create(doc, in_appearance_img_path);
705 widgetAnnot.createSignatureAppearance(img);
706
707 System.out.println("Testing timestamping configuration.");
708 TimestampingResult config_result = tst_config.testConfiguration(opts);
709 if (config_result.getStatus())
710 {
711 System.out.println("Success: timestamping configuration usable. Attempting to timestamp.");
712 }
713 else
714 {
715 // Print details of timestamping failure.
716 System.out.println(config_result.getString());
717 if (config_result.hasResponseVerificationResult())
718 {
719 EmbeddedTimestampVerificationResult tst_result = config_result.getResponseVerificationResult();
720 System.out.println(String.format("CMS digest status: %s", tst_result.getCMSDigestStatusAsString()));
721 System.out.println(String.format("Message digest status: %s", tst_result.getMessageImprintDigestStatusAsString()));
722 System.out.println(String.format("Trust status: %s", tst_result.getTrustStatusAsString()));
723 }
724 return false;
725 }
726
727 doctimestamp_signature_field.timestampOnNextSave(tst_config, opts);
728
729 // Save/signing throws if timestamping fails.
730 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
731
732 System.out.println("Timestamping successful. Adding LTV information for DocTimeStamp signature.");
733
734 // Add LTV information for timestamp signature to document.
735 VerificationResult timestamp_verification_result = doctimestamp_signature_field.verify(opts);
736 if (!doctimestamp_signature_field.enableLTVOfflineVerification(timestamp_verification_result))
737 {
738 System.out.println("Could not enable LTV for DocTimeStamp.");
739 return false;
740 }
741 doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);
742 System.out.println("Added LTV information for DocTimeStamp signature successfully.");
743
744 return true;
745 }
746
747
748}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales