Digitally Sign PDF Files - Go Sample Code

Requirements
View Demo

Sample code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files; provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our Server SDK and PDF Digital Signature Library.

Implementation steps

To apply and validate digital signatures with Apryse Server SDK:

Step 1: Follow get started with Server SDK in your preferred language or framework
Step 2: Add the sample code provided in this guide. Make note of directions at top of code samples.

To use this feature in production, your license key will need the Digital Signature Package. Trial keys already include all packages.

1//---------------------------------------------------------------------------------------
2// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
3// Consult LICENSE.txt regarding license information.
4//---------------------------------------------------------------------------------------
5
6////----------------------------------------------------------------------------------------------------------------------
7//// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
8////
9//// The following steps reflect typical intended usage of the digital signatures API:
10////
11//// 0. Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
12////
13//// 1. EITHER:
14//// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
15//// -OR-
16//// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
17//// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
18////
19//// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
20//// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
21//// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
22////
23//// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
24//// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
25//// certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
26//// is signed.]
27////
28//// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
29//// a. Taking a PKCS //12 keyfile path and its password
30//// b. Taking a buffer containing a PKCS //12 private keyfile and its password
31//// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
32//// in the following fashion:
33//// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
34//// validate/check a digital signature.
35//// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
36//// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
37//// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
38//// NOTE: It is only possible to sign/certify one signature per call to the Save function.
39////
40//// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
41//// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
42//// so as to not invalidate the other signature(s).
43////
44//// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
45//// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
46//// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
47////
48//// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
49//// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
50////----------------------------------------------------------------------------------------------------------------------
51
52package main
53import (
54 "fmt"
55 "strconv"
56 "flag"
57 "testing"
58 . "github.com/ApryseSDK/pdftron-go/v2"
59)
60
61var licenseKey string
62var modulePath string
63
64func init() {
65 flag.StringVar(&licenseKey, "license", "", "License key for Apryse SDK")
66 flag.StringVar(&modulePath, "modulePath", "", "Module path for Apryse SDK")
67}
68
69func VerifySimple(inDocpath string, inPublicKeyFilePath string) bool{
70 doc := NewPDFDoc(inDocpath)
71 fmt.Println("==========")
72 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
73
74 // Add trust root to store of trusted certificates contained in VerificationOptions.
75 opts.AddTrustedCertificate(inPublicKeyFilePath, uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))
76
77 result := doc.VerifySignedDigitalSignatures(opts)
78
79 if result == PDFDocE_unsigned{
80 fmt.Println("Document has no signed signature fields.")
81 return false
82 // e_failure == bad doc status, digest status, or permissions status
83 // (i.e. does not include trust issues, because those are flaky due to being network/config-related)
84 }else if result == PDFDocE_failure{
85 fmt.Println("Hard failure in verification on at least one signature.")
86 return false
87 }else if result == PDFDocE_untrusted{
88 fmt.Println("Could not verify trust for at least one signature.")
89 return false
90 }else if result == PDFDocE_unsupported{
91 // If necessary, call GetUnsupportedFeatures on VerificationResult to check which
92 // unsupported features were encountered (requires verification using 'detailed' APIs)
93 fmt.Println("At least one signature contains unsupported features.")
94 return false
95 // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
96 }else if result == PDFDocE_verified{
97 fmt.Println("All signed signatures in document verified.")
98 return true
99 }else{
100 fmt.Println("unrecognized document verification status")
101 return false
102 }
103}
104
105func VerifyAllAndPrint(inDocpath string, inPublicKeyFilePath string) bool{
106 doc := NewPDFDoc(inDocpath)
107 fmt.Println("==========")
108 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
109
110 // Trust the public certificate we use for signing.
111 trustedCertFile := NewMappedFile(inPublicKeyFilePath)
112 fileSz := trustedCertFile.FileSize()
113 fileReader := NewFilterReader(trustedCertFile)
114 trustedCertBuf := fileReader.Read(fileSz)
115 trustedCertBytes := make([]byte, int(trustedCertBuf.Size()))
116 for i := 0; i < int(trustedCertBuf.Size()); i ++ {
117 trustedCertBytes[i] = trustedCertBuf.Get(i)
118 }
119 opts.AddTrustedCertificate(&trustedCertBytes[0], int64(len(trustedCertBytes)), uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))
120
121 // Iterate over the signatures and verify all of them.
122 digsigFitr := doc.GetDigitalSignatureFieldIterator()
123 verificationStatus := true
124 for (digsigFitr.HasNext()){
125 curr := digsigFitr.Current()
126 result := curr.Verify(opts)
127 if result.GetVerificationStatus(){
128 fmt.Printf("Signature verified, objnum: %d\n", curr.GetSDFObj().GetObjNum())
129 }else{
130 fmt.Printf("Signature verification failed, objnum: %d\n", curr.GetSDFObj().GetObjNum())
131 verificationStatus = false
132 }
133 digest_algorithm := result.GetDigestAlgorithm()
134 if digest_algorithm == DigestAlgorithmE_SHA1{
135 fmt.Println("Digest algorithm: SHA-1")
136 }else if digest_algorithm == DigestAlgorithmE_SHA256{
137 fmt.Println("Digest algorithm: SHA-256")
138 }else if digest_algorithm == DigestAlgorithmE_SHA384{
139 fmt.Println("Digest algorithm: SHA-384")
140 }else if digest_algorithm == DigestAlgorithmE_SHA512{
141 fmt.Println("Digest algorithm: SHA-512")
142 }else if digest_algorithm == DigestAlgorithmE_RIPEMD160{
143 fmt.Println("Digest algorithm: RIPEMD-160")
144 }else if digest_algorithm == DigestAlgorithmE_unknown_digest_algorithm{
145 fmt.Println("Digest algorithm: unknown")
146 }else{
147 //unrecognized document status
148 }
149 fmt.Printf("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s\n",
150 result.GetDocumentStatusAsString(),
151 result.GetDigestStatusAsString(),
152 result.GetTrustStatusAsString(),
153 result.GetPermissionsStatusAsString())
154
155 changes := result.GetDisallowedChanges()
156 for i := 0; i < int(changes.Size()); i++ {
157 fmt.Printf("\tDisallowed change: %s, objnum: %d\n", changes.Get(i).GetTypeAsString(), changes.Get(i).GetObjNum())
158 }
159
160 // Get and print all the detailed trust-related results, if they are available.
161 if result.HasTrustVerificationResult(){
162 trustVerificationResult := result.GetTrustVerificationResult()
163 var msg string
164 if trustVerificationResult.WasSuccessful(){
165 msg = "Trust verified."
166 } else {
167 msg = "Trust not verifiable."
168 }
169 fmt.Println(msg)
170 fmt.Println(trustVerificationResult.GetResultString())
171
172 tmpTimeTv := trustVerificationResult.GetTimeOfTrustVerification()
173
174 trustVerificationTimeEnum := trustVerificationResult.GetTimeOfTrustVerificationEnum()
175
176 if trustVerificationTimeEnum == VerificationOptionsE_current{
177 fmt.Println("Trust verification attempted with respect to current time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
178 }else if trustVerificationTimeEnum == VerificationOptionsE_signing{
179 fmt.Println("Trust verification attempted with respect to signing time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
180 }else if trustVerificationTimeEnum == VerificationOptionsE_timestamp{
181 fmt.Println("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
182 }else{
183 //unrecognized time enum value
184 }
185
186 if trustVerificationResult.GetCertPath().Size() == 0{
187 fmt.Println("Could not print certificate path.")
188 }else{
189 fmt.Println("Certificate path:")
190 certPath := trustVerificationResult.GetCertPath()
191 for i := 0; i < int(certPath.Size()); i ++{
192 fmt.Println("\tCertificate:")
193 fmt.Println("\t\tIssuer names:")
194 issuerDn := certPath.Get(i).GetIssuerField().GetAllAttributesAndValues()
195 for j := 0; j < int(issuerDn.Size()); j ++{
196 fmt.Println("\t\t\t" + issuerDn.Get(j).GetStringValue())
197 }
198 fmt.Println("\t\tSubject names:")
199 subjectDn := certPath.Get(i).GetSubjectField().GetAllAttributesAndValues()
200 for k := 0; k < int(subjectDn.Size()); k ++{
201 fmt.Println("\t\t\t" + subjectDn.Get(k).GetStringValue())
202 }
203 fmt.Println("\t\tExtensions:")
204 for m := 0; m < int(certPath.Get(i).GetExtensions().Size()); m ++{
205 fmt.Println("\t\t\t" + certPath.Get(i).GetExtensions().Get(m).ToString())
206 }
207 }
208 }
209 }else{
210 fmt.Println("No detailed trust verification result available.")
211
212 unsupportedFeatures := result.GetUnsupportedFeatures()
213 if unsupportedFeatures.Size() > 0 {
214 fmt.Println("Unsupported features:")
215 for i := 0; i < int(unsupportedFeatures.Size()); i ++{
216 fmt.Println("\t" + unsupportedFeatures.Get(i))
217 }
218 }
219 }
220 fmt.Println("==========")
221
222 digsigFitr.Next()
223 }
224 return verificationStatus
225}
226
227func CertifyPDF(inDocpath string,
228 inCertFieldName string ,
229 inPrivateKeyFilePath string,
230 inKeyfilePassword string,
231 inAppearanceImagePath string,
232 inOutpath string){
233
234 fmt.Println("================================================================================")
235 fmt.Println("Certifying PDF document")
236
237 // Open an existing PDF
238 doc := NewPDFDoc(inDocpath)
239
240 if doc.HasSignatures(){
241 fmt.Println("PDFDoc has signatures")
242 }else{
243 fmt.Println("PDFDoc has no signatures")
244 }
245 page1 := doc.GetPage(1)
246
247 // Create a text field that we can lock using the field permissions feature.
248 annot1 := TextWidgetCreate(doc, NewRect(143.0, 440.0, 350.0, 460.0), "asdf_test_field")
249 page1.AnnotPushBack(annot1)
250
251 // Create a new signature form field in the PDFDoc. The name argument is optional;
252 // leaving it empty causes it to be auto-generated. However, you may need the name for later.
253 // Acrobat doesn"t show digsigfield in side panel if it's without a widget. Using a
254 // Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
255 certificationSigField := doc.CreateDigitalSignatureField(inCertFieldName)
256 widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), certificationSigField)
257 page1.AnnotPushBack(widgetAnnot)
258
259 // (OPTIONAL) Add an appearance to the signature field.
260 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImagePath)
261 widgetAnnot.CreateSignatureAppearance(img)
262
263 // Add permissions. Lock the random text field.
264 fmt.Println("Adding document permissions.")
265 certificationSigField.SetDocumentPermissions(DigitalSignatureFieldE_annotating_formfilling_signing_allowed)
266
267 // Prepare to lock the text field that we created earlier.
268 fmt.Println("Adding field permissions.")
269 testField := NewVectorString()
270 testField.Add("asdf_test_field")
271 certificationSigField.SetFieldPermissions(DigitalSignatureFieldE_include, testField)
272
273 certificationSigField.CertifyOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)
274
275 // (OPTIONAL) Add more information to the signature dictionary.
276 certificationSigField.SetLocation("Vancouver, BC")
277 certificationSigField.SetReason("Document certification.")
278 certificationSigField.SetContactInfo("www.pdftron.com")
279
280 // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
281 doc.Save(inOutpath, uint(0))
282
283 fmt.Println("================================================================================")
284}
285
286func SignPDF(inDocpath string,
287 inApprovalFieldName string,
288 inPrivateKeyFilePath string,
289 inKeyfilePassword string,
290 inAppearanceImgPath string,
291 inOutpath string){
292
293 fmt.Println("================================================================================")
294 fmt.Println("Signing PDF document")
295
296 // Open an existing PDF
297 doc := NewPDFDoc(inDocpath)
298
299 // Retrieve the unsigned approval signature field.
300 foundApprovalField := doc.GetField(inApprovalFieldName)
301 foundApprovalSignatureDigsigField := NewDigitalSignatureField(foundApprovalField)
302
303 // (OPTIONAL) Add an appearance to the signature field.
304 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
305 foundApprovalSignatureWidget := NewSignatureWidget(foundApprovalField.GetSDFObj())
306 foundApprovalSignatureWidget.CreateSignatureAppearance(img)
307
308 // Prepare the signature and signature handler for signing.
309 foundApprovalSignatureDigsigField.SignOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)
310
311 // The actual approval signing will be done during the following incremental save operation.
312 doc.Save(inOutpath, uint(SDFDocE_incremental))
313
314 fmt.Println("================================================================================")
315}
316
317func ClearSignature(inDocpath string,
318 inDigsigFieldName string,
319 inOutpath string){
320
321 fmt.Println("================================================================================")
322 fmt.Println("Clearing certification signature")
323
324 doc := NewPDFDoc(inDocpath)
325
326 digsig := NewDigitalSignatureField(doc.GetField(inDigsigFieldName))
327
328 fmt.Println("Clearing signature: " + inDigsigFieldName)
329 digsig.ClearSignature()
330
331 if !digsig.HasCryptographicSignature(){
332 fmt.Println("Cryptographic signature cleared properly.")
333 }
334 // Save incrementally so as to not invalidate other signatures from previous saves.
335 doc.Save(inOutpath, uint(SDFDocE_incremental))
336
337 fmt.Println("================================================================================")
338}
339
340func PrintSignaturesInfo(inDocpath string){
341 fmt.Println("================================================================================")
342 fmt.Println("Reading and printing digital signature information")
343
344 doc := NewPDFDoc(inDocpath)
345 if !doc.HasSignatures(){
346 fmt.Println("Doc has no signatures.")
347 fmt.Println("================================================================================")
348 return
349 }else{
350 fmt.Println("Doc has signatures.")
351 }
352
353 fitr := doc.GetFieldIterator()
354 for fitr.HasNext(){
355 current := fitr.Current()
356 if (current.IsLockedByDigitalSignature()){
357 fmt.Println("==========\nField locked by a digital signature")
358 }else{
359 fmt.Println("==========\nField not locked by a digital signature")
360 }
361 fmt.Println("Field name: " + current.GetName())
362 fmt.Println("==========")
363
364 fitr.Next()
365 }
366
367 fmt.Println("====================\nNow iterating over digital signatures only.\n====================")
368
369 digsigFitr := doc.GetDigitalSignatureFieldIterator()
370 for digsigFitr.HasNext(){
371 current := digsigFitr.Current()
372 fmt.Println("==========")
373 fmt.Println("Field name of digital signature: " + NewField(current.GetSDFObj()).GetName())
374
375 digsigfield := current
376 if !digsigfield.HasCryptographicSignature(){
377 fmt.Println("Either digital signature field lacks a digital signature dictionary, " +
378 "or digital signature dictionary lacks a cryptographic Contents entry. " +
379 "Digital signature field is not presently considered signed.\n" +
380 "==========")
381 digsigFitr.Next()
382 continue
383 }
384 certCount := digsigfield.GetCertCount()
385 fmt.Println("Cert count: " + strconv.Itoa(int(certCount)))
386 for i := uint(0); i < certCount; i ++{
387 cert := digsigfield.GetCert(i)
388 fmt.Println("Cert //" + strconv.Itoa(int(i)) + " size: " + strconv.Itoa(int(cert.Size())))
389 }
390 subfilter := digsigfield.GetSubFilter()
391
392 fmt.Println("Subfilter type: " + strconv.Itoa(int(subfilter)))
393
394 if subfilter != DigitalSignatureFieldE_ETSI_RFC3161{
395 fmt.Println("Signature's signer: " + digsigfield.GetSignatureName())
396
397 signingTime := digsigfield.GetSigningTime()
398 if signingTime.IsValid(){
399 fmt.Println("Signing time is valid.")
400 }
401 fmt.Println("Location: " + digsigfield.GetLocation())
402 fmt.Println("Reason: " + digsigfield.GetReason())
403 fmt.Println("Contact info: " + digsigfield.GetContactInfo())
404 }else{
405 fmt.Println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)")
406 }
407 if digsigfield.HasVisibleAppearance(){
408 fmt.Println("Visible")
409 }else{
410 fmt.Println("Not visible")
411 }
412 digsigDocPerms := digsigfield.GetDocumentPermissions()
413 lockedFields := digsigfield.GetLockedFields()
414 for i := 0; i < int(lockedFields.Size()); i ++{
415 fmt.Println("This digital signature locks a field named: " + lockedFields.Get(i))
416 }
417 if digsigDocPerms == DigitalSignatureFieldE_no_changes_allowed{
418 fmt.Println("No changes to the document can be made without invalidating this digital signature.")
419 }else if digsigDocPerms == DigitalSignatureFieldE_formfilling_signing_allowed{
420 fmt.Println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
421 }else if digsigDocPerms == DigitalSignatureFieldE_annotating_formfilling_signing_allowed{
422 fmt.Println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
423 }else if digsigDocPerms == DigitalSignatureFieldE_unrestricted{
424 fmt.Println("Document not restricted by this digital signature.")
425 }else{
426 fmt.Println("Unrecognized digital signature document permission level.")
427 }
428 fmt.Println("==========")
429 digsigFitr.Next()
430 }
431
432 fmt.Println("================================================================================")
433}
434
435func CustomSigningAPI(doc_path string,
436 cert_field_name string,
437 private_key_file_path string,
438 keyfile_password string,
439 public_key_file_path string,
440 appearance_image_path string,
441 digest_algorithm_type PdftronCryptoDigestAlgorithmType,
442 PAdES_signing_mode bool,
443 output_path string){
444 fmt.Println("================================================================================")
445 fmt.Println("Custom signing PDF document")
446
447 doc := NewPDFDoc(doc_path)
448
449 page1 := doc.GetPage(1)
450
451 digsig_field := doc.CreateDigitalSignatureField(cert_field_name)
452 widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), digsig_field)
453 page1.AnnotPushBack(widgetAnnot)
454
455 // (OPTIONAL) Add an appearance to the signature field.
456 img := ImageCreate(doc.GetSDFDoc(), appearance_image_path)
457 widgetAnnot.CreateSignatureAppearance(img)
458
459 signing_mode := DigitalSignatureFieldE_adbe_pkcs7_detached
460 if PAdES_signing_mode{
461 signing_mode = DigitalSignatureFieldE_ETSI_CAdES_detached
462 }
463 // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
464 digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
465 &signing_mode,
466 7500) // For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be, in bytes.
467 // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.
468
469 // (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
470 current_date := NewDate()
471 current_date.SetCurrentTime()
472 digsig_field.SetSigDictTimeOfSigning(current_date)
473
474 doc.Save(output_path, uint(SDFDocE_incremental))
475
476 // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
477 pdf_digest := digsig_field.CalculateDigest(digest_algorithm_type)
478
479 signer_cert := NewX509Certificate(public_key_file_path)
480
481 // Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes.
482 // The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm.
483 pades_versioned_ess_signing_cert_attribute := DigitalSignatureFieldGenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)
484
485 // Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS).
486 // The signedAttrs are certain attributes that become protected by their inclusion in the signature.
487 signedAttrs := DigitalSignatureFieldGenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)
488
489 // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
490 signedAttrs_digest := DigestAlgorithmCalculateDigest(digest_algorithm_type, signedAttrs)
491
492 ///////////////////////////// custom digest signing starts ////////////////////////////
493 // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
494 // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
495 signature_value := DigestAlgorithmSignDigest(
496 signedAttrs_digest,
497 digest_algorithm_type,
498 private_key_file_path,
499 keyfile_password)
500 ///////////////////////////// custom digest signing ends ///////////////////////////////
501
502 // Then, load all your chain certificates into a container of X509Certificate.
503 chain_certs := NewVectorX509Certificate()
504
505 // Then, create ObjectIdentifiers for the algorithms you have used.
506 // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
507 digest_algorithm_oid := NewObjectIdentifier(digest_algorithm_type)
508 signature_algorithm_oid := NewObjectIdentifier(ObjectIdentifierE_RSA_encryption_PKCS1)
509
510 // Then, put the CMS signature components together.
511 cms_signature := DigitalSignatureFieldGenerateCMSSignature(
512 signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
513 signature_value, signedAttrs)
514
515 // Write the signature to the document.
516 doc.SaveCustomSignature(cms_signature, digsig_field, output_path)
517
518 fmt.Println("================================================================================")
519}
520
521func TimestampAndEnableLTV(inDocpath string,
522 inTsaUrl string,
523 inTrustedCertPath string,
524 inAppearanceImgPath string,
525 inOutpath string) bool{
526 doc := NewPDFDoc(inDocpath)
527 doctimestampSignatureField := doc.CreateDigitalSignatureField()
528 tstConfig := NewTimestampingConfiguration(inTsaUrl)
529 opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
530// It is necessary to add to the VerificationOptions a trusted root certificate corresponding to
531// the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
532// response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this
533// function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp
534// in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.
535
536 opts.AddTrustedCertificate(inTrustedCertPath)
537// By default, we only check online for revocation of certificates using the newer and lighter
538// OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However,
539// it may be necessary to enable online CRL revocation checking in order to verify some timestamps
540// (i.e. those that do not have an OCSP responder URL for all non-trusted certificates).
541
542 opts.EnableOnlineCRLRevocationChecking(true)
543
544 widgetAnnot := SignatureWidgetCreate(doc, NewRect(0.0, 100.0, 200.0, 150.0), doctimestampSignatureField)
545 doc.GetPage(1).AnnotPushBack(widgetAnnot)
546
547 // (OPTIONAL) Add an appearance to the signature field.
548 img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
549 widgetAnnot.CreateSignatureAppearance(img)
550
551 fmt.Println("Testing timestamping configuration.")
552 configResult := tstConfig.TestConfiguration(opts)
553 if configResult.GetStatus(){
554 fmt.Println("Success: timestamping configuration usable. Attempting to timestamp.")
555 }else{
556 // Print details of timestamping failure.
557 fmt.Println(configResult.GetString())
558 if configResult.HasResponseVerificationResult(){
559 tstResult := configResult.GetResponseVerificationResult()
560 fmt.Println("CMS digest status: "+ tstResult.GetCMSDigestStatusAsString())
561 fmt.Println("Message digest status: " + tstResult.GetMessageImprintDigestStatusAsString())
562 fmt.Println("Trust status: " + tstResult.GetTrustStatusAsString())
563 }
564 return false
565 }
566
567 doctimestampSignatureField.TimestampOnNextSave(tstConfig, opts)
568
569 // Save/signing throws if timestamping fails.
570 doc.Save(inOutpath, uint(SDFDocE_incremental))
571
572 fmt.Println("Timestamping successful. Adding LTV information for DocTimeStamp signature.")
573
574 // Add LTV information for timestamp signature to document.
575 timestampVerificationResult := doctimestampSignatureField.Verify(opts)
576 if !doctimestampSignatureField.EnableLTVOfflineVerification(timestampVerificationResult){
577 fmt.Println("Could not enable LTV for DocTimeStamp.")
578 return false
579 }
580 doc.Save(inOutpath, uint(SDFDocE_incremental))
581 fmt.Println("Added LTV information for DocTimeStamp signature successfully.")
582
583 return true
584}
585
586func TestDigitalSignatures(t *testing.T){
587 // Initialize PDFNet
588 PDFNetInitialize(licenseKey)
589
590 result := true
591 inputPath := "../TestFiles/"
592 outputPath := "../TestFiles/Output/"
593
594 //////////////////////////////////////// TEST 0:
595 // Create an approval signature field that we can sign after certifying.
596 // (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
597 // Open an existing PDF
598 doc := NewPDFDoc(inputPath + "waiver.pdf")
599 widgetAnnotApproval := SignatureWidgetCreate(doc, NewRect(300.0, 287.0, 376.0, 306.0), "PDFTronApprovalSig")
600 page1 := doc.GetPage(1)
601 page1.AnnotPushBack(widgetAnnotApproval)
602 doc.Save(outputPath + "waiver_withApprovalField_output.pdf", uint(SDFDocE_remove_unused))
603
604 //////////////////////////////////////// TEST 1: certify a PDF.
605 CertifyPDF(inputPath + "waiver_withApprovalField.pdf",
606 "PDFTronCertificationSig",
607 inputPath + "pdftron.pfx",
608 "password",
609 inputPath + "pdftron.bmp",
610 outputPath + "waiver_withApprovalField_certified_output.pdf")
611 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_output.pdf")
612
613 //////////////////////////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
614 SignPDF(inputPath + "waiver_withApprovalField_certified.pdf",
615 "PDFTronApprovalSig",
616 inputPath + "pdftron.pfx",
617 "password",
618 inputPath + "signature.jpg",
619 outputPath + "waiver_withApprovalField_certified_approved_output.pdf")
620 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_output.pdf")
621
622 //////////////////////////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
623 ClearSignature(inputPath + "waiver_withApprovalField_certified_approved.pdf",
624 "PDFTronCertificationSig",
625 outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
626 PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
627
628 //////////////////////////////////////// TEST 4: Verify a document's digital signatures.
629 if !VerifyAllAndPrint(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
630 result = false
631 }
632
633 //////////////////////////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
634 if !VerifySimple(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
635 result = false
636 }
637
638 //////////////////// TEST 6: Custom signing API.
639 // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
640 // which allows users to customize the process of signing documents. Among other things, this
641 // includes the capability to allow for easy integration of PDF-specific signing-related operations
642 // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
643 // to system keystores, etc.
644 CustomSigningAPI(inputPath + "waiver.pdf",
645 "PDFTronApprovalSig",
646 inputPath + "pdftron.pfx",
647 "password",
648 inputPath + "pdftron.cer",
649 inputPath + "signature.jpg",
650 DigestAlgorithmE_SHA256,
651 true,
652 outputPath + "waiver_custom_signed.pdf")
653
654 //////////////////////////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
655
656 // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
657 // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
658 // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
659 // tsaUrl := "YOUR_URL_OF_TSA"
660 //
661 // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
662 // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
663 // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
664 // trustedCertPath := "YOUR_CERTIFICATE";
665 //
666 // if tsaUrl == "YOUR_URL_OF_TSA" {
667 // fmt.Println("Error: The URL of your timestamp authority was not specified.")
668 // result = false
669 // } else if trustedCertPath == "YOUR_CERTIFICATE" {
670 // fmt.Println("Error: The path to your timestamp authority trusted root certificate was not specified.")
671 // result = false
672 // } else if !TimestampAndEnableLTV(inputPath + "waiver.pdf",
673 // tsaUrl,
674 // trustedCertPath,
675 // inputPath + "signature.jpg",
676 // outputPath + "waiver_DocTimeStamp_LTV.pdf") {
677 // result = false
678 // }
679
680 //////////////////////////////////////// End of tests. ////////////////////////////////////////
681
682 if !result{
683 fmt.Println("Tests FAILED!!!\n==========")
684 PDFNetTerminate()
685 return
686 }
687 PDFNetTerminate()
688 fmt.Println("Tests successful.\n==========")
689}

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales