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