Digitally Sign PDF Files - Go Sample Code

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.

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