More languages
Some test text!
More languages
Sample Swift code to use PDFTron SDK's high-level digital signature API for digitally signing and/or certifying PDF files. Learn more about our Swift PDF Library and PDF Digital Signature Library.
Get Started Samples DownloadTo run this sample, get started with a free trial of Apryse SDK.
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2020 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
// 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)).
//
// 1. EITHER:
// (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
// -OR-
// (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the
// document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//
// 2. Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found.
// If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
// NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//
// [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions)
// or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow
// certain types of changes to be made to the document without invalidating the cryptographic digital signature's hash once it
// is signed.]
//
// 4. Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
// a. Taking a PKCS #12 keyfile path and its password
// b. Taking a buffer containing a PKCS #12 private keyfile and its password
// c. Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
// in the following fashion:
// i) Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or
// validate/check a digital signature.
// ii) Create an instance of the implemented SignatureHandler and register it with PDFDoc with
// pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
// iii) Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
// NOTE: It is only possible to sign/certify one signature per call to the Save function.
//
// 5. Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic hash to it.
// IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
// so as to not invalidate the other signature's('s) cryptographic hashes.
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------
// To build and run this sample, please specify OpenSSL include & lib paths to the Makefile
//
// If OpenSSL is installed elsewhere, it may be necessary to add the path to the headers in the $(INCLUDE) variable as
// well as the location of either libcrypto.a or libcrypto.so/libcrypto.dylib.
// Note for iOS development: This code can be used to digitally sign PDFs in iOS devices. When using the code signing
// part of this code, it will be necessary to compile OpenSSL for iOS.
import PDFNet
import Foundation
// EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support/
func VerifyAllAndPrint(_ in_docpath: String?, _ in_public_key_file_path: String?) -> Bool {
let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
print("==========")
let opts: PTVerificationOptions = PTVerificationOptions(level: e_ptcompatibility_and_archiving)
// Trust the public certificate we use for signing.
let trusted_cert_file: PTMappedFile = PTMappedFile(filename: in_public_key_file_path)
let file_sz = trusted_cert_file.fileSize()
let file_reader: PTFilterReader = PTFilterReader(filter: trusted_cert_file)
let trusted_cert_buf = file_reader.read(file_sz)
opts.addTrustedCertificate(trusted_cert_buf)
// Iterate over the signatures and verify all of them.
let digsig_fitr: PTDigitalSignatureFieldIterator = doc.getDigitalSignatureFieldIterator()
var verification_status = true
while digsig_fitr.hasNext() {
defer {
digsig_fitr.next()
}
let curr: PTDigitalSignatureField = digsig_fitr.current()
let result: PTVerificationResult = curr.verify(opts)
if result.getVerificationStatus() {
print("Signature verified, objnum: \(curr.getSDFObj().getNum())")
} else {
print("Signature verification failed, objnum: \(curr.getSDFObj().getNum())")
verification_status = false
}
switch result.getSignersDigestAlgorithm() {
case e_ptsha1:
print("Digest algorithm: SHA-1")
case e_ptsha256:
print("Digest algorithm: SHA-256")
case e_ptsha384:
print("Digest algorithm: SHA-384")
case e_ptsha512:
print("Digest algorithm: SHA-512")
case e_ptripemd160:
print("Digest algorithm: RIPEMD-160")
case e_ptunknown_digest_algorithm:
print("Digest algorithm: unknown")
default:
let e = NSException(name: NSExceptionName("unrecognized digest algorithm"), reason: "unrecognized digest algorithm", userInfo: nil)
e.raise()
}
print("Detailed verification result: ")
switch result.getDocumentStatus() {
case e_ptno_error:
print("\tNo general error to report.")
case e_ptcorrupt_file:
print("\tSignatureHandler reported file corruption.")
case e_ptunsigned:
print("\tThe signature has not yet been cryptographically signed.")
case e_ptbad_byteranges:
print("\tSignatureHandler reports corruption in the ByteRanges in the digital signature.")
case e_ptcorrupt_cryptographic_contents:
print("\tSignatureHandler reports corruption in the Contents of the digital signature.")
default:
let e = NSException(name: NSExceptionName("unrecognized document status"), reason: "unrecognized document status", userInfo: nil)
e.raise()
}
switch result.getDigestStatus() {
case e_ptdigest_invalid:
print("\tThe digest is incorrect.")
case e_ptdigest_verified:
print("\tThe digest is correct.")
case e_ptdigest_verification_disabled:
print("\tDigest verification has been disabled.")
case e_ptweak_digest_algorithm_but_digest_verifiable:
print("\tThe digest is correct, but the digest algorithm is weak and not secure.")
case e_ptno_digest_status:
print("\tNo digest status to report.")
case e_ptunsupported_encoding:
print("\tNo installed SignatureHandler was able to recognize the signature's encoding.")
default:
let e = NSException(name: NSExceptionName("unrecognized digest status"), reason: "unrecognized digest status", userInfo: nil)
e.raise()
}
switch result.getTrustStatus() {
case e_pttrust_verified:
print("\tEstablished trust in signer successfully.")
case e_ptuntrusted:
print("\tTrust could not be established.")
case e_pttrust_verification_disabled:
print("\tTrust verification has been disabled.")
case e_ptno_trust_status:
print("\tNo trust status to report.")
default:
let e = NSException(name: NSExceptionName("unrecognized trust status"), reason: "unrecognized trust status", userInfo: nil)
e.raise()
}
switch result.getPermissionsStatus() {
case e_ptinvalidated_by_disallowed_changes:
print("\tThe document has changes that are disallowed by the signature's permissions settings.")
case e_pthas_allowed_changes:
print("\tThe document has changes that are allowed by the signature's permissions settings.")
case e_ptunmodified:
print("\tThe document has not been modified since it was signed.")
case e_ptpermissions_verification_disabled:
print("\tPermissions verification has been disabled.")
case e_ptno_permissions_status:
print("\tNo permissions status to report.")
default:
let e = NSException(name: NSExceptionName("unrecognized modification permissions status"), reason: "unrecognized modification permissions status", userInfo: nil)
e.raise()
}
let changes: [PTDisallowedChange] = result.getDisallowedChanges()
for change in changes {
print("\tDisallowed change: \(change.getTypeAsString() ?? ""), objnum: \(change.getObjNum())")
}
if result.hasTrust() {
let trust_verification_result: PTTrustVerificationResult = result.getTrust()
print(trust_verification_result.wasSuccessful() ? "Trust verified." : "Trust not verifiable.")
print("Trust verification result string: \(trust_verification_result.getString() ?? "")")
let tmp_time_t = trust_verification_result.getTimeOfTrustVerification()
switch trust_verification_result.getTimeOfTrustVerificationEnum() {
case e_ptcurrent:
print(String(format: "Trust verification attempted with respect to current time (as epoch time): %lu", tmp_time_t))
case e_ptsigning:
print(String(format: "Trust verification attempted with respect to signing time (as epoch time): %lu", tmp_time_t))
case e_pttimestamp:
print(String(format: "Trust verification attempted with respect to secure embedded timestamp (as epoch time): %lu", tmp_time_t))
default:
let e = NSException(name: NSExceptionName("unrecognized time enum value"), reason: "unrecognized time enum value", userInfo: nil)
e.raise()
}
} else {
print("No detailed trust verification result available.")
}
print("==========")
}
return verification_status
}
func CertifyPDF(_ in_docpath: String?, _ in_cert_field_name: String?, _ in_private_key_file_path: String?, _ in_keyfile_password: String?, _ in_appearance_image_path: String?, _ in_outpath: String?) {
print("================================================================================")
print("Certifying PDF document")
// Open an existing PDF
let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
print("PDFDoc has \(doc.hasSignatures() ? "signatures" : "no signatures")")
let page1: PTPage = doc.getPage(1)
// Create a text field that we can lock using the field permissions feature.
let annot1 = PTTextWidget.create(doc, pos: PTPDFRect(x1: 50, y1: 550, x2: 350, y2: 600), field_name: "asdf_test_field")
page1.annotPushBack(annot1)
/* Create a new signature form field in the PDFDoc. The name argument is optional;
leaving it empty causes it to be auto-generated. However, you may need the name for later.
Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
let certification_sig_field: PTDigitalSignatureField = doc.createDigitalSignatureField(in_cert_field_name)
let widgetAnnot: PTSignatureWidget = PTSignatureWidget.create(withDigitalSignatureField: doc, pos: PTPDFRect(x1: 0, y1: 100, x2: 200, y2: 150), field: certification_sig_field)
page1.annotPushBack(widgetAnnot)
// (OPTIONAL) Add an appearance to the signature field.
let img = PTImage.create(withFile: doc.getSDFDoc(), filename: in_appearance_image_path, encoder_hints: PTObj())
widgetAnnot.createSignatureAppearance(img)
// Prepare the document locking permission level. It will be applied upon document certification.
print("Adding document permissions.")
certification_sig_field.setDocumentPermissions(e_ptannotating_formfilling_signing_allowed)
// Prepare to lock the text field that we created earlier.
print("Adding field permissions.")
var fields_to_lock: [String] = []
fields_to_lock.append("asdf_test_field")
certification_sig_field.setFieldPermissions(e_ptdigsig_permission_include, in_field_names: fields_to_lock)
certification_sig_field.certify(onNextSave: in_private_key_file_path, in_password: in_keyfile_password)
// (OPTIONAL) Add more information to the signature dictionary.
certification_sig_field.setLocation("Vancouver, BC")
certification_sig_field.setReason("Document certification.")
certification_sig_field.setContactInfo("www.pdftron.com")
// Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
doc.save(toFile: in_outpath, flags: 0)
print("================================================================================")
}
func SignPDF(_ in_docpath: String?, _ in_approval_field_name: String?, _ in_private_key_file_path: String?, _ in_keyfile_password: String?, _ in_appearance_img_path: String?, _ in_outpath: String?) {
print("================================================================================")
print("Signing PDF document")
// Open an existing PDF
let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
// Retrieve the unsigned approval signature field.
let found_approval_field: PTField = doc.getField(in_approval_field_name)
let found_approval_signature_digsig_field: PTDigitalSignatureField = PTDigitalSignatureField(in_field: found_approval_field)
// (OPTIONAL) Add an appearance to the signature field.
let img: PTImage = PTImage.create(withFile: doc.getSDFDoc(), filename: in_appearance_img_path, encoder_hints: PTObj())
let found_approval_signature_widget: PTSignatureWidget = PTSignatureWidget(d: found_approval_field.getSDFObj())
found_approval_signature_widget.createSignatureAppearance(img)
// Prepare the signature and signature handler for signing.
found_approval_signature_digsig_field.sign(onNextSave: in_private_key_file_path, in_password: in_keyfile_password)
// The actual approval signing will be done during the following incremental save operation.
doc.save(toFile: in_outpath, flags: e_ptincremental.rawValue)
print("================================================================================")
}
func ClearSignature(_ in_docpath: String?, _ in_digsig_field_name: String?, _ in_outpath: String?) {
print("================================================================================")
print("Clearing certification signature")
let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
let digsig: PTDigitalSignatureField = PTDigitalSignatureField(in_field: doc.getField(in_digsig_field_name))
print("Clearing signature: \(in_digsig_field_name ?? "")")
digsig.clearSignature()
if !digsig.hasCryptographicSignature() {
print("Cryptographic signature cleared properly.")
}
// Save incrementally so as to not invalidate other signatures' hashes from previous saves.
doc.save(toFile: in_outpath, flags: e_ptincremental.rawValue)
print("================================================================================")
}
func PrintSignaturesInfo(_ in_docpath: String?) {
print("================================================================================")
print("Reading and printing digital signature information")
let doc: PTPDFDoc = PTPDFDoc(filepath: in_docpath)
if !doc.hasSignatures() {
print("Doc has no signatures.")
print("================================================================================")
return
} else {
print("Doc has signatures.")
}
let fitr: PTFieldIterator = doc.getFieldIterator()
while fitr.hasNext() {
defer {
fitr.next()
}
print("==========")
if fitr.current().isLockedByDigitalSignature() {
print("Field locked by a digital signature")
} else {
print("Field not locked by a digital signature")
}
print("Field name: \(fitr.current().getName() ?? "")")
print("==========")
}
print("====================")
print("Now iterating over digital signatures only.")
print("====================")
let digsig_fitr: PTDigitalSignatureFieldIterator = doc.getDigitalSignatureFieldIterator()
while digsig_fitr.hasNext() {
defer {
digsig_fitr.next()
}
print("==========")
print("Field name of digital signature: \(PTField(field_dict: digsig_fitr.current().getSDFObj()).getName() ?? "")")
let digsigfield: PTDigitalSignatureField = digsig_fitr.current()
if !digsigfield.hasCryptographicSignature() {
print("""
Either digital signature field lacks a digital signature dictionary, \
or digital signature dictionary lacks a cryptographic hash entry. \
Digital signature field is not presently considered signed.
""")
print("==========")
continue
}
let cert_count = digsigfield.getCertCount()
print("Cert count: \(cert_count)")
for i in 0..<cert_count {
let cert: Data = digsigfield.getCert(i)
print(String(format: "Cert #%d size: %lu", i, cert.count))
}
let subfilter = digsigfield.getSubFilter()
print("Subfilter type: \(subfilter)")
if subfilter != e_ptETSI_RFC3161 {
print("Signature's signer: \(digsigfield.getSignatureName() ?? "")")
let signing_time: PTDate = digsigfield.getSigningTime()
if signing_time.isValid() {
print("Signing day: \((signing_time as TRN_date).getDay())")
}
print("Location: \(digsigfield.getLocation() ?? "")")
print("Reason: \(digsigfield.getReason() ?? "")")
print("Contact info: \(digsigfield.getContactInfo() ?? "")")
} else {
print("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)")
}
print((digsigfield.hasVisibleAppearance()) ? "Visible" : "Not visible")
let digsig_doc_perms = digsigfield.getDocumentPermissions()
let locked_fields: [String] = digsigfield.getLockedFields()
for field_name in locked_fields {
print("This digital signature locks a field named: \(field_name)")
}
switch digsig_doc_perms {
case e_ptno_changes_allowed:
print("No changes to the document can be made without invalidating this digital signature.")
case e_ptformfilling_signing_allowed:
print("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
case e_ptannotating_formfilling_signing_allowed:
print("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
case e_ptunrestricted:
print("Document not restricted by this digital signature.")
default:
//NSException.raise("Unrecognized digital signature document permission level.", format: "Unrecognized digital signature document permission level.")
print()
}
print("==========")
}
print("================================================================================")
}
func runDigitalSignaturesTest() -> Int {
return autoreleasepool {
var result = true
let input_path: String = (Bundle.main.resourcePath)! + ("/")
let output_path: String = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("").path
//////////////////// TEST 0:
/* Create an approval signature field that we can sign after certifying.
(Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
do {
try PTPDFNet.catchException {
let doc: PTPDFDoc = PTPDFDoc(filepath: "\(input_path)\("tiger.pdf")")
let approval_signature_field: PTDigitalSignatureField = doc.createDigitalSignatureField("PDFTronApprovalSig")
let widgetAnnotApproval: PTSignatureWidget = PTSignatureWidget.create(withDigitalSignatureField: doc, pos: PTPDFRect(x1: 300, y1: 300, x2: 500, y2: 200), field: approval_signature_field)
let page1: PTPage = doc.getPage(1)
page1.annotPushBack(widgetAnnotApproval)
doc.save(toFile: "\(output_path)\("tiger_withApprovalField_output.pdf")", flags: e_ptremove_unused.rawValue)
}
} catch {
print("Exception: \(error)\n")
result = false
}
do {
try PTPDFNet.catchException {
CertifyPDF("\(input_path)\("tiger_withApprovalField.pdf")",
"PDFTronCertificationSig",
"\(input_path)\("pdftron.pfx")",
"password",
"\(input_path)\("pdftron.bmp")",
"\(output_path)\("tiger_withApprovalField_certified_output.pdf")")
PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_output.pdf")")
}
} catch {
print("Exception: \(error)\n")
result = false
}
//////////////////// TEST 2: sign a PDF with a certification and an unsigned signature field in it.
do {
try PTPDFNet.catchException {
SignPDF("\(input_path)\("tiger_withApprovalField_certified.pdf")",
"PDFTronApprovalSig",
"\(input_path)\("pdftron.pfx")",
"password",
"\(input_path)\("signature.jpg")",
"\(output_path)\("tiger_withApprovalField_certified_approved_output.pdf")")
PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_approved_output.pdf")")
}
} catch {
print("Exception: \(error)\n")
result = false
}
//////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
do {
try PTPDFNet.catchException {
ClearSignature("\(input_path)\("tiger_withApprovalField_certified_approved.pdf")",
"PDFTronCertificationSig",
"\(output_path)\("tiger_withApprovalField_certified_approved_certcleared_output.pdf")")
PrintSignaturesInfo("\(output_path)\("tiger_withApprovalField_certified_approved_certcleared_output.pdf")")
}
} catch {
print("Exception: \(error)\n")
result = false
}
//////////////////// TEST 4: Verify a document's digital signatures.
do {
try PTPDFNet.catchException {
// EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support/
if !VerifyAllAndPrint("\(input_path)\("tiger_withApprovalField_certified_approved.pdf")", "\(input_path)\("pdftron.cer")") {
result = false
}
}
} catch {
print("Exception: \(error)\n")
result = false
}
//////////////////// End of tests. ////////////////////
if result {
print("Tests successful.")
print("==========")
} else {
print("Tests FAILED!!!")
print("==========")
}
return result ? 1 : 0
}
}