Digital Signatures

What Are Digital Signatures?

A digital signature is an electronic fingerprint uniquely identifying the signing person. For more information on digital signatures, please look at the Digital Signatures in a PDF guide by Adobe. You should also take a look at the 12.8 Digital Signatures section of Adobe’s PDF 1.7 Specification.

For general information on how digital signatures work, you’ll find more information here.

Considerations

PSPDFKit allows signing both existing signature form elements and documents without a signature form element.

Supported signing algorithms are:

  • RSA
  • ECDSA

Supported hashing algorithms include:

  • MD4
  • MD5
  • SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512-256)

A hashing algorithm is an algorithm that converts a large amount of data (your PDF document) to a fixed-length string. If someone changes one bit of data in your PDF document, the hashing algorithm produces a different string, so if you store that hash inside the PDF document, you could potentially know when it has been changed by a third party. However, as easy as it sounds, this approach is insecure. If someone knows the hashing algorithm that was used to sign the PDF, they could replace the hash inside the PDF so that it validates as authentic. To avoid this, PSPDFKit works with encryption algorithms.

More specifically, PSPDFKit supports RSA, one of the most popular public key encryption algorithms. ECDSA, an algorithm based on elliptic curve cryptography, is also supported. Cryptographic systems based on ECDSA are becoming the de facto standard for messaging and systems security. For example, in the iOS Security Guide, Apple explains how it uses ECDSA extensively in its mobile operating system, along with detailing its benefits when compared to other algorithms. You can use OpenSSL to generate a self-signed RSA certificate using this OpenSSL command:

1
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem

ECDSA certificates can be created using the openssl ecparam command.

You can use a self-signed certificate for testing purposes, but you will need to make sure the certificate is trusted by all the devices the PDF is opened on (including PCs/Macs with Acrobat). A self-signed certificate will probably also generate warnings about its keyUsage extension (the self-signed certificate must permit certificate signing — keyCertSign, see RFC 5280).

In production, always use a certificate from a valid certificate authority. Make sure the certificate’s keyUsage has the digitalSignature permission set (see RFC 5280).

How to Create Digital Signatures

To create a digital signature, you need two things:

  • An X509 certificate that contains your public key and your signer information. PSPDFKit supports PEM-encoded and DER-encoded X509 certificates, as well as DER-encoded PKCS#7. Note: PEM-encoded PKCS#7 certificates are not yet supported and will trigger a decoding error. You can check the encoding of a certificate file by using the OpenSSL command line tool as follows:
1
  openssl pkcs7 -noout -text -print_certs -in example.p7b

The above command should not return an error if “example.p7b” is a PEM-encoded PKCS#7 certificate or certificate chain.

1
  openssl pkcs7 -inform der -noout -text -print_certs -in example.p7b

The above command should not return an error if “example.p7b” is a DER-encoded PKCS#7 certificate or certificate chain.

  • Your private key.

The signing process produces the signature by encrypting the message digest from the PDF file with a private key. The certificate with its public key is added to the signature and saved in the PDF file. For your convenience, PSPDFKit provides a PSPDFPKCS12Signer that loads a certificate with the public and private key from a p12 archive. If you want to customize the signing process, you need to subclass PSPDFSigner. Keep in mind that certificates installed by a user via opening the .p12 container with built-in apps (via Install Profile) will go to the Apple access group and will only be available to Apple-provided apps such as Safari or Mail. See the Apple Technical Q&A for more details and a suggested workaround.

Here is an example of how to register a PSPDFPKCS12Signer:

Copy
1
2
3
4
5
6
7
8
9
// p12Data is a p12 archive NSData object.
let p12 = PSPDFPKCS12(data: p12Data)

// Create a signer with a display name. The display name will show up in the list of identities when you tap on a signature form field.
let p12Signer = PSPDFPKCS12Signer(displayName: "John Appleseed", pkcs12: p12)

// Register your signer with the signature manager.
let signatureManager = PSPDFKit.sharedInstance.signatureManager
signatureManager.register(p12Signer)
Copy
1
2
3
4
5
6
7
8
9
// p12Data is a p12 archive NSData object.
PSPDFPKCS12 *p12 = [[PSPDFPKCS12 alloc] initWithData:p12Data];

// Create a signer with a display name. The display name will show up in the list of identities when you tap on a signature form field.
PSPDFPKCS12Signer *p12Signer = [[PSPDFPKCS12Signer alloc] initWithDisplayName:@"John Appleseed" PKCS12:p12];

// Register your signer with the signature manager.
PSPDFSignatureManager *signatureManager = PSPDFKit.sharedInstance.signatureManager;
[signatureManager registerSigner:p12Signer];

User Interface

When creating a signature in the user interface, you need to either provide a certificate or draw an ink, or both. The document is only digitally signed if a certificate has been provided. Otherwise, only the ink signature is added to the document.

By default, the user is able to select a certificate if there are registeredSigners available. This behavior can be customized via PSPDFConfiguration.signatureCertificateSelectionMode.

Signing with a Form Element

If there are no stored signatures in the shared PSPDFSignatureStore, then tapping an empty signature form element shows a PSPDFSignatureViewController, which is where you can both sign a document with an ink signature and assign a certificate to your signature.

If there are already stored signatures, a PSPDFSignatureSelectorViewController is shown instead, allowing you to choose an existing signature or create a new one.

Signing without a Form Element

If a document doesn’t have a signature form element, you can still digitally sign it. The way to initiate this flow is to tap on the signature toolbar button and then select or create a signature as shown above. If you added a certificate to the signature, you will be asked to select the signature placement on the page; otherwise, the signature is placed into the document and you can move and resize it to your liking. After making the selection, you will be asked for the password of the certificate. Finally, after entering the password, the document will be signed.

Biometric Signatures

Beginning with PSPDFKit 7.0 for iOS, we automatically store biometric properties encrypted in the digital signature. These biometric properties are stored via PSPDFSignatureBiometricProperties and include data points about the creation of the ink signature. If no ink signature was used to digitally sign a document, the biometric properties will be empty.

By default, all available properties on PSPDFSignatureBiometricProperties are populated and stored. These properties can be customized to restrict storing specific properties via PSPDFConfiguration.signatureBiometricPropertiesOptions. When signing a document, only the specified values are stored in the digital signature.

For example, to only store the touch radius and the input method biometric properties of the ink signature, you can use the following code:

Copy
1
2
3
let controller = PSPDFViewController(document: document, configuration: PSPDFConfiguration { builder in
    builder.signatureBiometricPropertiesOptions = [.touchRadius, .inputMethod]
})
Copy
1
2
3
PSPDFViewController *controller = [[PSPDFViewController alloc] initWithDocument:document configuration:[PSPDFConfiguration configurationWithBuilder:^(PSPDFConfigurationBuilder *builder) {
    builder.signatureBiometricPropertiesOptions = PSPDFSignatureBiometricPropertiesOptionTouchRadius | PSPDFSignatureBiometricPropertiesOptionInputMethod;
}]];

You can customize the properties that are stored by overriding -[PSPDFSignatureContainer initWithAnnotation:signer:biometricProperties:] and modifying the biometricProperties object to fit your needs.

The following biometric properties and data point are captured and stored in the digital signature when using the default document signing UI flow.

If you are creating digital signatures manually, you can pass a PSPDFSignatureBiometricProperties object to -[PSPDFSigner signFormElement:withCertificate:writeTo:appearance:biometricProperties:completionBlock:], which is then saved alongside the digital signature — encrypted with the signing certificate — in the document.

You can access biometric properties from a PSPDFSignatureFormElement object after the signing process is complete by using -[PSPDFSignatureFormElement signatureBiometricProperties:], like so:

Copy
1
2
3
4
5
6
7
let signedSignatureFormElement: PSPDFSignatureFormElement = ...
var privateKey: PSPDFPrivateKey = ...
let pkcs12: PSPDFPKCS12 = ...
pkcs12.unlock(withPassword: password) { _, key, _ in
    privateKey = key
}
let biometricProperties = signedSignatureFormElement.signatureBiometricProperties(privateKey)
Copy
1
2
3
4
5
6
7
8
PSPDFSignatureFormElement *signedSignatureFormElement = ...
PSPDFPrivateKey *privateKey = ...
PSPDFPKCS12 *pkcs12 = ...
[pkcs12 unlockWithPassword:password done:^(PSPDFX509 *x509, PSPDFPrivateKey *pkey, NSError *err) {
    privateKey = pkey;
}];

PSPDFSignatureBiometricProperties *biometricProperties = [signedSignatureFormElement signatureBiometricProperties:privateKey];

Signature Validation

PSPDFKit also needs to be able to validate signatures. The validation process consists of two steps.

  • In the first step, we check if the signature certificate embedded during signing can be trusted. In order to do this, we need to obtain the trusted certificate from the authority that issued it, a root CA certificate, or an intermediate CA certificate. For your convenience, PSPDFKit already provides the Adobe Root CA. This CA is also typically loaded by third-party readers.

  • In the second step, we verify the signature. This process essentially decrypts the signature with a public key from the certificate embedded in the PDF file on signing and compares it with the message digest built from the PDF file excluding the signature itself.

Here is an example of how to provide the trusted certificate:

Copy
1
2
3
4
5
6
7
8
9
10
11
do {
    // Load a certificate (with a public key) from a p7 archive.
    let certificateData = try Data(contentsOf: p7URL)
    let certificates = try PSPDFX509.certificatesFromPKCS7Data(certificateData)

    for certificate in certificates {
        signatureManager.addTrustedCertificate(certificate)
    }
} catch {
    // Don't forget to check for errors here.
}
Copy
1
2
3
4
5
6
7
8
9
10
11
// Load a certificate (with a public key) from a p7 archive.
NSData *certificateData = [NSData dataWithContentsOfURL:p7URL];

NSError *error = nil;
NSArray *certificates = [PSPDFX509 certificatesFromPKCS7Data:certData error:&err];

// Don't forget to check for errors here.

for (PSPDFX509 *certificate in certificates) {
    [signatureManager addTrustedCertificate:certificate];
}

You can also take a look at the digital signing process examples in the PSPDFCatalog projects.

Customizing Digital Signing

PSPDFKit offers various options to customize the digital signing experience, primarily via PSPDFDigitalSignatureCoordinator subclassing hooks.

A document that will be digitally signed will be copied, signed, and saved to a new path to keep the (unsigned) original document intact. You can customize the path where the digitally signed document should be saved by subclassing PSPDFDigitalSignatureCoordinator and overriding pathForDigitallySignedDocumentFromOriginalDocument:suggestedFileName: to return a custom path. The suggested file name parameter is based on the title of the original document, if available, or the file name as a fallback. It can be used as the last path component in the path where the digitally signed document should be stored. The returned path will be sanitized, and we will append the .pdf path extension.

You can also customize how the signed document should be presented. By default, this is done by pushing a new PSPDFViewController instance on the navigation controller with the digitally signed document shown. This can be customized by overriding presentSignedDocument:showingPageIndex: in your PSPDFDigitalSignatureCoordinator subclass. By overriding this method, you are responsible for presenting/pushing the document that should be part of your digital signing experience. The pageIndex parameter is the page index of the signature form field of the document, which you have the option of scrolling to.

Customizing the Signature Appearance

During the signing process, you can customize how the final signature will be shown in the document.

PSPDFDigitalSignatureCoordinator provides configureSignatureAppearanceWithBuilder:document:signature:, which you can override to customize the signature appearance.

PSPDFSignatureAppearance has some Boolean properties to decide whether the signer’s name, date of signing, signature location, or signature reason should be shown after the document is signed.

The appearanceMode property enables further configuration of the visual appearance, with two supported modes: PSPDFSignatureAppearanceModeSignatureAndDescription, and PSPDFSignatureAppearanceModeDescriptionOnly. If your signature field has some appearance you want to keep after it’s signed (for example, a border or watermark logo), set the reuseExistingAppearance property.

You can provide your own artwork (for example, a handwritten signature) that will be displayed on the left part of a signature field when its appearance mode is PSPDFSignatureAppearanceModeSignatureAndDescription. Set the signatureGraphic property to type PSPDFAnnotationAppearanceStream to accomplish that. You can create a PSPDFAnnotationAppearanceStream with either a JPEG image or a PDF document.

If you are signing a document programmatically, you can pass the PSPDFSignatureAppearance class to any of the signing methods in PSPDFSigner.

When providing an ink signature, the default signature appearance looks like this:

Creating a Custom Signer

In order to customize the signing process, you can subclass PSPDFSigner. Then register the signer with the signature manager, as described above. Please have a look at the documentation in the PSPDFSigner header file for more information on what methods you need to override.

Signing a Document Using Your Own Encryption

Sometimes it is not feasible to have access to a private key and load it on an iOS device to sign a document. Or, you may want more control over the signing process than what the PSPDFSigner class provides by default — for example, to sign information using a web service. For those situations, the externalSignatureDelegate property of PSPDFSigner may be useful.

First create a class that implements the PSPDFExternalSignature protocol, and set it as the externalSignatureDelegate property of PSPDFSigner or one of its subclasses. There are two methods you should implement:

  • - (NSData *)signData:hashAlgorithm: will be called during the signing process with two pieces of information. The data parameter is the hashed document information that must be signed. PSPDFKit follows the security recommendations of the PDF standard and always hashes the whole PDF document, except for a space reserved for the signature itself. hashAlgorithm is the hash function used to convert the raw bytes in the PDF document into NSData. This method should return the signed data using the private key associated with the certificate’s public key you passed to signFormElement: in PSPDFSigner.

  • - (PSPDFSignatureEncryptionAlgorithm)encryptionAlgorithm will also be called during the signing process, and you should return the encryption algorithm you intend to use to encrypt the signature. The PSPDFSignatureEncryptionAlgorithm enumerated type documents every encryption algorithm that is supported.

How Does Adobe Acrobat Validate a Document Signed By PSPDFKit?

Once you have signed a PDF document using PSPDFKit, you’ll notice that after opening it in Adobe Acrobat, you might get the following validation status:

This warning is informing you that the reader application has verified that the document has not been modified since the last time it was signed, but that the certificate is not yet trusted. You can configure the certificate as a trusted anchor in Adobe Acrobat by following the instructions provided by Adobe.

Alternatively, if the PDF is opened in a Windows environment, you can configure Adobe Acrobat to automatically trust every certificate in the Windows Certificate Store by clicking Edit > Preferences > Security > Advanced Preferences, and then checking Enable searching the Windows Certificate Store for the following operations. Use this option with caution, as it might pose a security risk.

Adobe Acrobat 9 introduced a new program to make the validation process more user-friendly: the Adobe Approved Trust List (AATL). Businesses that provide certificates to their users can apply to this program by submitting application materials and their root certificates. Once approved, Adobe Acrobat will automatically download and trust every certificate that is part of this program.