Sign PDFs and Add Custom Encryption on iOS

There are various use cases in which PSPDFKit’s default signing implementation can’t be used. Some example use cases are when:

  • You’re required to use a specific crypto library.

  • You aren’t in direct possession of a signing key (for example, when using a hardware security module (HSM) or a signing service).

  • You have a specific multi-step signing flow that isn’t supported by PSPDFKit’s default implementation (for example, with multiple passwords).

To customize the signing process, you have to add a custom implementation of the DataSigning protocol. An instance of this protocol’s implementation should be passed on while signing the document using the Document.sign(formElement:configuration:outputDataProvider:) API.

Signing a Document Using Your Own Encryption

Sometimes it’s 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’s provided by default — for example, to sign information using a web service. In such situations, the sign(unsignedData:hashAlgorithm:) method of DataSigning is useful. Another alternative is to use the contained digital signatures workflow explained later in this guide.

First, create a class that implements the DataSigning protocol and its method, sign(unsignedData:hashAlgorithm:). This method will be called during the signing process with the unsigned data and the hashing algorithm to be used. You must return the signed data and its format (SignedDataFormat) after carrying out your signing using your own process, where SignedDataFormat defines the format of the signed data returned.

PSPDFKit supports using the signed data that’s encapsulated in a PKCS#7 signature container when .pkcs7 is used for the SignedDataFormat. Signed data returned unwrapped in its original form should use the SignedDataFormat.genericSignedData option. PSPDFKit follows the security recommendations of the PDF standard and always hashes an entire PDF document, except for the space reserved for the signature itself.

You can customize the hash algorithm used for signing the document by setting it on the SigningConfiguration instance:

// Custom implementation of `DataSigning` to carry out the signing.
private class CustomDataSigner: DataSigning {
    func sign(unsignedData: Data, hashAlgorithm: PDFSignatureHashAlgorithm) async throws -> (signedData: Data, dataFormat: PSPDFKit.SignedDataFormat) {
        // Carry out your custom data signing.
        let signedData = ...

        // Return the signed data specifying the data format.
        // Use `.pkcs7` if the data has been wrapped in a PKCS#7 signature container.
        // Otherwise, use `genericSignedData`.
        return (signedData, .genericSignedData)
    }
}

// Carrying out the signing using the above custom implementation.
// Initialize the custom data signing implementation.
let customSigner = CustomDataSigner()

// Use the `SigningConfiguration` API to provide the custom signer and the certificates
let configuration = SigningConfiguration(dataSigner: customSigner, certificates: publicCertificates, hashAlgorithm: .SHA512)

// Sign the document with the signing configuration that states the use of the custom signing implementation.
try await unsignedDocument.sign(formElement: signatureFormElement, configuration: configuration, outputDataProvider: FileDataProvider(fileURL: signedDocumentURL))