Digital Signatures

A digital signature is an electronic fingerprint uniquely identifying the signing person. A digital signature on a PDF document is both reliable proof of the document’s origin and protection against modification by third parties. Be sure to check out our blog post about electronic signatures in a PDF, which explains how digital signatures work and when they are needed.

ℹ️ Note: 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, please read the digital signature entry on Wikipedia.


Considerations

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

The supported signing method is:

  • CMS (adobe.pkcs7.detached)

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. 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).

⚠️ Important: 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.

  • First, you need 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 certificates. 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 will print an error message if “example.p7b” is not a PEM-encoded PKCS#7 certificate or certificate chain.

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

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

  • Second, you need 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. The Signer class allows signing of documents by adding a digital signature to a SignatureFormField. Its methods, signFormField() and signFormFieldAsync(), allow both computation and saving of digital signatures to a definable output file.

PSPDFKit ships two default implementations of the Signer class, but you can always create a custom signer if your use case requires it. The two signers are:

  • MemorySigner, which takes a KeyStore.PrivateKeyEntry that was loaded by your app and uses it to sign the document directly.
  • Pkcs12Signer, which loads the signing certificate from a PKCS#12 file (usually having the .p12 file extension) to sign the document. If necessary, the signer will request a password for unlocking the PKCS#12 file, in order to load the required keys from it:
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Create a signer loading the signing certificate from a PKCS#12 file.
// The provided `displayName` can be used by PSPDFKit in various views.
val signer: Signer = Pkcs12Signer(
	"John Appleseed",
	Uri.parse("file:///android_asset/JohnAppleseed.p12")
)

// Sign the form field, writing the signed document to a destination.
// You can optionally provide additional biometric signature data.
signer.signFormField(
	formField,
	biometricSignatureData,
	destination,
	object : Signer.OnSigningCompleteCallback() {
		override fun onSigningFailed(ex: Exception) {
			// Handle signing errors here...
		}

		override fun onSigningCompleted() {
			// The document was successfully signed!
		}
	}
);
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Create a signer loading the signing certificate from a PKCS#12 file.
// The provided `displayName` can be used by PSPDFKit in various views.
final Signer signer = new Pkcs12Signer(
	"John Appleseed",
	Uri.parse("file:///android_asset/JohnAppleseed.p12")
);

// Sign the form field, writing the signed document to a destination.
// You can optionally provide additional biometric signature data.
signer.signFormField(
	formField,
	biometricSignatureData,
	destination,
	new Signer.OnSigningCompleteCallback() {
		@Override
		public void onSigningFailed(Exception ex) {
			if (!e.isDisposed()) e.onError(ex);
		}

		@Override
		public void onSigningCompleted() {
			if (!e.isDisposed()) e.onComplete();
		}
	}
);

💡 Tip: For an interactive example of digital signatures, check out DigitalSignatureExample in the Catalog app.

Implementing the Custom Signature Provider

The SignatureProvider interface defines entities capable of signing data of a PDF. A custom signature provider has to implement two methods:

  • getEncryptionAlgorithm() has to return the encryption algorithm that will be applied to sign the PDF. The signature provider can return any of the available EncryptionAlgorithm values. Usually this is dependent on the private key used (i.e. whether you use an RSA or DSA key).
  • signData() has to perform the actual signing operation. This involves digesting the given PDF data using the requested HashAlgorithm and then encrypting it using the signer’s private key (using the selected encryption algorithm).
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CustomSignatureProvider: SignatureProvider {
	/**
	 * For example, this uses Java's crypto APIs for signing a PDF.
	 * The `getSignatureAlgorithm()` method will find the appropriate
	 * signing algorithm. You can see this example in our Catalog app.
	 */
	override fun signData(data: ByteArray, hashAlgorithm: HashAlgorithm): ByteArray
		= Signature.getInstance(getSignatureAlgorithm(hashAlgorithm)).run {
			initSign(signingKey.privateKey)
			update(data)
			sign()
		}

	override fun getEncryptionAlgorithm() = EncryptionAlgorithm.RSA
}
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CustomSignatureProvider implements SignatureProvider {
	/**
	 * For example, this uses Java's crypto APIs for signing a PDF.
	 * The `getSignatureAlgorithm()` method will find the appropriate
	 * signing algorithm. You can see this example in our Catalog app.
	 */
	@NonNull
	@Override
	public byte[] signData(@NonNull byte[] data, @NonNull HashAlgorithm hashAlgorithm) {
		try {
			final Signature rsa = Signature.getInstance(getSignatureAlgorithm(hashAlgorithm));
			rsa.initSign(signingKey.getPrivateKey());
			rsa.update(data);
			return rsa.sign();
		} catch (Exception e) {
			...
		}
	}

	@NonNull
	@Override
	public EncryptionAlgorithm getEncryptionAlgorithm() {
		return EncryptionAlgorithm.RSA;
	}
}

How to Use the Digital Signatures UI

PSPDFKit comes with a ready-to-use user interface collection that allows you to provide digital signing capabilities inside your apps with minimal effort. When creating a signature in the user interface, you need to provide a certificate or draw an ink signature, or both. The document is only digitally signed if a certificate has been provided. Otherwise, only the ink signature is added to the document. All you need to do is register all Signer instances you would like to provide your users with by using the addSigner() from the SignatureManager class. PSPDFKit will then pick up these signers to use them in the signature dialog:

Copy
1
2
val johnAppleseed: Signer = Pkcs12Signer("John Appleseed", Uri.parse("file:///android_asset/JohnAppleseed.p12"))
SignatureManager.addSigner("john-appleseed", johnAppleseed)
Copy
1
2
final Signer johnAppleseed = new Pkcs12Signer("John Appleseed", Uri.parse("file:///android_asset/JohnAppleseed.p12"));
SignatureManager.addSigner("john-appleseed", johnAppleseed);

After registering the signer, PSPDFKit will automatically show it inside the SignaturePickerFragment as an available signer (using its display name).

💡 Tip: To see an example of this, check out DocumentSigningExample in the Catalog app.

Signing with a Form Element

If there are no stored signatures in the shared SignatureStorage, then tapping an empty signature form element will show the signature dialog presented by SignaturePickerFragment, 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, the SignaturePickerFragment will show a picker instead, allowing you to either 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.

Retrieving a Signed Document After a User Signs It

When a user signs a document by clicking on a SignatureFormField and choosing a signature to sign the document with, PSPDFKit will write the signed document to a temporary location. To get access to the signed document, you need to register a DocumentSigningListener on the PdfFragment:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pdfFragment.setDocumentSigningListener(object : DocumentSigningListener {
    override fun onDocumentSigned(signedDocumentUri: Uri) {
        // Move the signed document to its permanent location.
        // Then reopen it.
    }

    override fun onDocumentSigningError(error: Throwable?) {
        // An error occurred during signing.
    }

    override fun onSigningCancelled() {
        // The signing process was canceled.
    }
})
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
getPdfFragment().setDocumentSigningListener(new DocumentSigningListener() {
    @Override
    public void onDocumentSigned(@NonNull Uri signedDocumentUri) {
        // Move the signed document to its permanent location.
        // Then reopen it.
    }

    @Override
    public void onDocumentSigningError(@Nullable Throwable error) {
        // An error occurred during signing.
    }

    @Override
    public void onSigningCancelled() {
        // The signing process was canceled.
    }
});

Using the Signature Picker Fragment

The SignaturePickerFragment is fully integrated into the PdfActivity and the PdfFragment. However, it can also be used independently, which allows you to create and collect Signature instances outside of the context of a document:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
SignaturePickerFragment.show(
	fragmentManager,
	object : SignaturePickerFragment.OnSignaturePickedListener {
		override fun onSignaturePicked(signature: Signature) {
			// The user selected a signature.
		}

		override fun onDismiss() {
			// The user dismissed the dialog without selecting a signature.
		}
	}
)
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SignaturePickerFragment.show(
	fragmentManager,
	new OnSignaturePickedListener() {
		@Override
		public void onSignaturePicked(@NonNull final Signature signature) {
			// The user selected a signature.
		}

		@Override
		public void onDismiss() {
			// The user dismissed the dialog without selecting a signature.
		}
	}
);

The Signature is a container for the following properties of the created signature:

  • The ink annotation data, which is the handwritten signature of the user.
  • An optional unique identifier of a registered Signer. This is the same identifier used to register the signer using SignatureManager.addSigner().
  • An optional BiometricSignatureData holding additional information about the signature (see section below).

How to Validate a Digital Signature

The DigitalSignatureValidator extracts the DigitalSignatureInfo from a signed SignatureFormField and will perform a validation check on the signature. Validation can be performed using validateSignature() and validateSignatureAsync() and will return a DigitalSignatureValidationResult once complete. The result will hold information about both the validity of the digital signature and a variety of possible validation warnings and errors:

Copy
1
2
3
4
5
6
val result = DigitalSignatureValidator.validate(signatureFormField)
if (result.validationStatus == ValidationStatus.SUCCESS) {
	onSignatureValidated()
} else {
	showValidationProblems(result.problems)
}
Copy
1
2
3
4
5
6
final DigitalSignatureValidationResult result = DigitalSignatureValidator.validate(signatureFormField);
if (result.getValidationStatus() == ValidationStatus.SUCCESS) {
	onSignatureValidated();
} else {
	showValidationProblems(result.getProblems());
}

What Are the Different Signature Validation Statuses?

If you validate a digital signature with PSPDFKit, either programmatically or using our UI, there can be several possible statuses for the signature. Programmatically, the DigitalSignatureValidationResult class contains an enum, DigitalSignatureValidationResult.DocumentIntegrityStatus, that can be queried to determine if the document was altered in some way after it was digitally signed.

Let’s take a look at our validation UI when the document is signed with a self-signed certificate:

In this sample case, the first paragraph informs us of the general status of the digital signature. Next, the signer name and signing date is shown. This information always comes from the certificate itself, so you don’t have programmatic access to modify it. Following that paragraph, the integrity status of the document is displayed. In this case, the document has not been modified since it was signed, which is a good sign. Finally, the UI lets us know that the certificate used for signing was a self-signed certificate. This is not necessarily bad, but we find out about this situation with a severity level of “warning.”

Let’s see what happens if we try to validate a signature with an expired certificate:

In this case, we are informed that the certificate is not valid because it has expired. To prevent confusing our users, our default UI does not show the integrity status of a document if the certificate validation failed (that is, if the general signature status is “error”).

This is the validation UI that is shown when the certificate is not self-signed or hasn’t expired but is not yet trusted by PSPDFKit:

How to Remove a Digital Signature

If you want to remove a signature again, you just need to access the signed SignatureFormField and call removeSignature() or removeSignatureAsync(). This will remove the DigitalSignatureInfo from the given SignatureFormField.

Biometric Signatures

BiometricSignatureData is “real-world information” that can be attached to a digital signature. This information includes things like whether or not the signature was created with a stylus, the size of the signee’s finger, and the timing and pressure information that was collected while writing the signature. Ultimately, this data can be used to create solutions that provide a higher grade of security than traditional digital signatures do. A digital signature can only contain biometric data if an ink signature was used to create it.

Creating Biometric Data

You can create a BiometricSignatureData instance using its Builder. All values of the biometric data are optional and can be left out. Once created, the BiometricSignatureData is immutable:

Copy
1
2
3
4
val biometricData = BiometricSignatureData.Builder()
	.setInputMethod(BiometricSignatureData.InputMethod.FINGER)
	.setPressurePoints(listOf(0.4f, 0.1f, 0.94f, 0.6f))
	.build()
Copy
1
2
3
4
final BiometricSignatureData biometricData = new BiometricSignatureData.Builder()
	.setInputMethod(BiometricSignatureData.InputMethod.FINGER)
	.setPressurePoints(Arrays.asList(new Float[]{ 0.4f, 0.1f, 0.94f, 0.6f }))
	.build();

💡 Tip: BiometricSignatureData is a Parcelable; this allows it to be passed around activities or saved to your instance state.

Collecting Biometric Data

When a user creates a Signature using the SignaturePickerFragment, the signature will also hold BiometricSignatureData that was collected during the creation of the signature. You can retrieve this data using signature.getBiometricData():

1
2
// Retrieve the biometric data that was collected during signature creation.
val biometricData = signature.biometricData
Copy
1
2
// Retrieve the biometric data that was collected during signature creation.
final BiometricSignatureData biometricData = signature.getBiometricData();

Digitally Signing with Biometric Data

To add biometric data to a digital signature, pass it to your Signer during the signing process. The Signer will automatically verify the biometric data and attach it to the signature:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val biometricData = signature.biometricData
// Simply pass in `BiometricSignatureData` as an extra argument.
signer.signFormField(
	formField,
	biometricData,
	outputFile,
	object : Signer.OnSigningCompleteCallback() {
		override fun onSigningFailed(ex: Exception) {
			// Handle signing errors here...
		}

		override fun onSigningCompleted() {
			// The document was successfully signed!
		}
	})
})
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final BiometricSignatureData biometricData = signature.getBiometricData();
// Simply pass in `BiometricSignatureData as` an extra argument.
signer.signFormField(
	formField,
	biometricData,
	destination,
	new Signer.OnSigningCompleteCallback() {
		@Override
		public void onSigningFailed(Exception ex) {
			// Handle signing errors here...
		}

		@Override
		public void onSigningCompleted() {
			// The document was successfully signed!
		}
	}
);

External Signature Providers

PSPDFKit supports external signature providers such as hardware security modules (HSMs) and other signing entities. The SignatureProvider interface defines classes capable of signing PDF blob data, which is required as part of the digital signing process of a PDF.

The default implementation used by PSPDFKit, aka the PrivateKeySignatureProvider, signs PDF data using a java.security.KeyStore.PrivateKeyEntry. Both Pkcs12Signer and MemorySigner derive from PrivateKeySignatureProvider and can be used if your app stores the private key of the user directly on the device. If you are using an external signing service (such as an HSM), implement a custom Signer as shown in CustomSignatureProviderExample.

Custom Signature Storage

By default, PSPDFKit will store any signature for which your users have selected the Store Signature option in the primary PSPDFKit SQLite database of your app. If you would like to use a different signature storage method, you can provide it using the setSignatureStorage() method of your PdfFragment before the signature dialog is shown (for example, inside the onCreate() method of your activity):

Copy
CustomPdfActivity.kt
1
2
3
4
5
6
7
8
9
10
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // You can create any number of signature storage databases.
    val storage: SignatureStorage = DatabaseSignatureStorage
        .withName("custom-storage")

    // Configure your custom storage method in the fragment.
    pdfFragment.setSignatureStorage(storage)
}
Copy
CustomPdfActivity.java
1
2
3
4
5
6
7
8
9
10
 @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // You can create any number of signature storage databases.
    final SignatureStorage storage = DatabaseSignatureStorage
        .withName("custom-storage");

    // Configure your custom storage method in the fragment.
    getPdfFragment().setSignatureStorage(storage);
}

💡 Tip: If you’d like to implement custom signature storage — for example, writing to an encrypted database — you can write your own custom implementation of the SignatureStorage interface and provide instances of it to PSPDFKit using the setSignatureStorage(SignatureStorage) method.

Customizing Digital Signing

Custom Signers

There are various use cases in which PSPDFKit’s default signing implementation can’t be used. In such cases, developers can create custom Signer implementations. Some example use cases are when:

  • You are required to use a specific crypto library (for example, Bouncy Castle).
  • You aren’t in direct possession of a signing key (for example, when using an HSM or a signing service).
  • You have a specific multi-step signing flow that is not supported by PSPDFKit’s default implementation (for example, with multiple passwords).

Implementing a Custom Signer

To build a custom signer, create a class that derives from Signer and implement the abstract prepareSigningParameters() method. The method needs to load all parameters required for a successful signing operation. These parameters are:

  • The X509Certificate that is going to be embedded into the signed PDF document.
  • A SignatureProvider that will perform the actual signing operation of the PDF data. Implement this to support virtually any entity capable of signing data in a document.

Whenever PSPDFKit tries to digitally sign a PDF using your custom signer, its prepareSigningParameters() method will be called. It is the responsibility of the signer to retrieve all signing parameters and return them using the provided callback. For example:

Copy
1
2
3
4
5
6
override fun prepareSigningParameters(callback: OnSigningParametersReadyCallback) {
	// Load all parameters.
	loadSigningParameters()
	// Call this whenever your signer is ready. This will finish the signing process.
	callback.onSigningParametersReady(signatureProvider, certificate)
}
Copy
1
2
3
4
5
6
7
@Override
protected void prepareSigningParameters(@NonNull OnSigningParametersReadyCallback callback) {
	// Load all parameters.
	loadSigningParameters();
	// Call this whenever your signer is ready. This will finish the signing process.
	callback.onSigningParametersReady(signatureProvider, certificate);
}

Contained Digital Signatures

PSPDFKit also supports a workflow where the creation of a digital signature is split in two phases:

  • First, you can “prepare” a document with a signature form field by stamping a custom signature appearance and reserving space in the PDF for the digital signature.
  • Second, you can “embed” a custom PKCS#7 signature container in a document that was already “prepared” in the first step. The end result is a digitally signed document.

This workflow, which we call contained digital signatures, is especially useful when the cryptographic material to sign a document (keys, certificates) is not available on the platform that executes PSPDFKit.

For the first part, preparing a document, you may call prepareFormFieldForSigningAsync() on the Signer class. This preparation step allows customization for the raw content that will fill the digital signature contents before a real digital signature is applied: The SignatureContents interface is an abstraction for that. If you want to fill the digital signature contents with binary zeroes, pass an instance of the BlankSignatureContents class to signatureContents() when building SignerOptions used in prepareFormFieldForSigningAsync().

Note that the document generated by the prepareFormFieldForSigningAsync() API has two important properties: First, it’s not a valid digitally signed document yet, so it may show errors if you try to validate it with PSPDFKit or any other third-party tool. Second, this document must not be modified in any way, in order to prevent corrupting the digital signature that will be embedded in the final document. The process of embedding the real digital signature is explained next.

For embedding a real digital signature in a document generated by prepareFormFieldForSigningAsync(), you can call embedSignatureInFormFieldAsync() on the Signer class. You are responsible for generating a valid digital signature in the cryptographic PKCS#7 format for the prepared document. In order to do that, pass a custom implementation of SignatureContents to the previously cited method. In your SignatureContents#signData() implementation, you will receive the part of the document that you need to hash, encrypt, and package into a digital signature. You can call getHashForDocumentRange() on a PdfDocument instance to help you calculate the hash value of some parts of a PDF document. You can generate the PKCS7 signature container with the help of our PKCS7 class or use PKCS7SignatureContents directly.

PSPDFKit also ships with the ready-to-use ContainedSignaturesSigner, which encapsulates the contained signatures flow. You can start using it by implementing its abstract method, prepareSignatureContents(). This method receives the document prepared for signing with the prepareFormFieldForSigningAsync(). You should return your custom implementation of SignatureContents from this method to embed it as a digital signature in the prepared document.

For a complete example, refer to ContainedSignaturesExample in the Catalog app.

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.

Troubleshooting

I Have a Chain of Certificates That I Used for Signing but When I Validate the Digital Signature in PSPDFKit It Shows as “Not Trusted”

When you have a chain of certificates comprising a leaf certificate and some intermediate certificate authorities, we require that you explicitly trust every intermediate certificate authority in the chain. If any certificate in the path is not trusted, we can’t perform proper path validation, and as a result, we will show two possible error messages:

  • If there is not any expired certificate in the chain, we show a warning explaining that we couldn’t verify the chain of certificates.
  • If there is an expired certificate in the chain, we show an error because we consider an expired certificate to be a very important issue during digital signature validation.

In order to trust certificates, you can use the addTrustedCertificates() method. Note that, by default, our SDK will automatically add and trust those CAs already included in the Android device, but you will need to manually trust any other CA that issued your certificate.

I Am Signing a Document with Multiple Signatures and Previous Signatures Show as “Not Valid”

When digitally signing documents with multiple signature fields, you need to make sure that the document can be saved incrementally. When incremental saving is disabled or not supported, PSPDFKit rewrites already signed document data, which could result in the invalidation of existing signatures.

Incremental saving is enabled only for documents that are not password protected with document sources that support appending. Appendable sources include local files and WritableDataProvider instances that return true inside WritableDataProvider#supportsAppending().