Migrating to the Advanced Digital Signatures API

This guide covers migrating from the old digital signing API using PDFSigner to the new advanced digital signatures API. This API, which was released alongside PSPDFKit 13.2 for iOS, is available as a method on Document.

The sign(_:withCertificate... APIs of PDFSigner will soon be deprecated, but the new API has complete support for PAdES signatures, including timestamping support. PDFSigner allows passing a name, which is considered unsafe, as this could be interpreted as a form of identity theft. The newer API mitigates this risk by using the name saved in the signing certificate.

We strongly encourage you to migrate to the new advanced digital signatures API, as it not only provides extensive functionality, but it’s also more streamlined and easier to use.

A subset of features on PDFSigner and its subclasses should be migrated to the advanced digital signatures API as described below.

Signing a FormElement

For advanced digital signatures, use Document.sign(formElement:configuration:outputDataProvider:) to sign a form field instead of the sign(_:withCertificate... APIs available on PDFSigner and its subclasses. Note that the often-used APIs — such as sign(_:usingPassword:writeTo:) and its variants on PKCS12Signer, which is a subclass of PDFSigner — will be deprecated soon.

The new API, like the outgoing PDFSigner API, takes the SignatureFormElement to be signed and a data provider implementing DataProviding to specify the destination of the signed document. The entire configuration of the signature is housed under the new SigningConfiguration API, which aids discoverability. The private key and the certificate for signing need to be provided to the SigningConfiguration instance instead of the PDFSigner.sign(_:withCertificate:...) API:

let configuration: SigningConfiguration = ...
try await unsignedDocument.sign(formElement: signatureFormElement, configuration: configuration, outputDataProvider: FileDataProvider(fileURL: signedDocumentURL))

Using SigningConfiguration for Signature Customizations

Specify the signature type (PDFSignatureType) using SigningConfiguration instead of the PDFSigner instance.

Customizations such as signature appearance, biometric data, estimated size, and hashing algorithm, which required implementing PDFDocumentSignerDataSource, have now been added to the SigningConfiguration API that’s supplied while signing the element.

Instead of implementing the PDFDocumentSignerDataSource protocol, you can provide the signature appearance, biometric data, estimated size, hashing algorithm, and more to the SigningConfiguration instance directly. The signature appearance and biometric data are still represented using the same types of PDFSignatureAppearance and PDFSignatureBiometricProperties, respectively.

With this new API, the encryption algorithm is now chosen automatically based on the signing configuration, so you no longer have to specify it like before with PDFSigner APIs.

The reason for signing and the location of signing should also be provided to the SigningConfiguration instance instead of the PDFSigner instance.

The API also supports timestamping by providing the URL for the timestamping authority server to the SigningConfiguration instance.

All of the above-mentioned customization options are optional, and the SigningConfiguration only requires its data signer and signing certificates to be specified for intialization. The certificates still use the same API of X509. The data signer is an instance that implements the DataSigning protocol. In some cases, this will be the private key (PrivateKey) instance that you’ll have after unlocking a PKCS#12 blob using the PKCS12.unlockCertificateChain(withPassword:) API:

// Default initializer.
let defaultInitConfig = SigningConfiguration(dataSigner: privateKey, certificates: certificates)
let privateKey: PrivateKey = ...
let certificates: [X509] = ...
let timestampSource: URL = ...
let customAppearance: PDFSignatureAppearance = ...
let biometricData: PDFSignatureBiometricProperties = ...

// Custom initializer with all components.
let signingConfiguration = SigningConfiguration(type: .pades, dataSigner: privateKey, certificates: certificates, hashAlgorithm: .SHA256, appearance: customAppearance, estimatedSize: 16_384, reason: "I approve", location: "End of the Universe", timestampSource: timestampSource, biometricData: biometricData)

Custom Signing

The DataSigning protocol mentioned above comes in handy when you want to implement your custom signing — for example, if you want to send your data to a server for signing. Use a custom implementation of DataSigning.sign(unsignedData:hashAlgorithm:) instead of implementing the PDFDocumentSignerDelegate API:

// 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)
    }
}