Add a Digital Signature to a Document

PSPDFKit supports digitally signing documents by using a private key to encrypt the hash of the snapshot of the current state of the document. The certificate with its public key is added to the signature and saved in the PDF file.

PSPDFKit gives you the byte range and a hash representation of the current state of the document, so you can digitally sign it by obtaining a DER PKCS#7 container from either the byte range or the hash and then applying that DER PKCS#7 container to a new, ad hoc invisible signature form field.

You’re only responsible for providing the signing service that will receive the byte range and the hash as input values and return the DER PKCS#7 container to be applied to the prepared signature form field. Our implementation allows you to produce, validate, and display digitally signed documents in a totally flexible way.

ℹ️ Note: If you want to use the Digital Signatures feature, make sure to include it in your license. Please follow this link to contact sales to start using it.

Creating a Digital Signature

Adding a digital signature on a PDF document is both reliable proof of the document’s origin and protection against modification by third parties.

To create a digital signature, you need two things.

  • First, you need an X.509 certificate that contains your public key and your signer information. PSPDFKit supports PEM-encoded and DER-encoded X.509 certificates, as well as DER-encoded PKCS#7 certificates. You can verify if a PKCS#7 certificate file is correctly PEM-encoded by using the OpenSSL command-line tool as follows:

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.

To verify if a PKCS#7 certificate file is correctly DER encoded, you can use this command instead:

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. A self-signed private key and certificate pair can be created with the command shown in the previous section.

Signing Process

Under the hood, the process of signing a document via PSPDFKit Server is divided into three phases:

  1. PSPDFKit Server prepares the document for a signature, adding an invisible form field that will contain the signature value.

  2. PSPDFKit Server then contacts an external signing service you’re responsible for setting up, which will provide a compliant signature value.

  3. PSPDFKit Server applies the returned signature to the document and saves it, storing the final file as an asset associated with the document and the used Instant layer.

This architecture ensures that PSPDFKit doesn’t need access to the private key that ultimately will be used to produce the signature value, leaving you complete freedom to choose which strategy to use to manage its lifecycle and security.

The Signing Service

The signing service is a network service that you’re responsible for maintaining and operating.

It needs to expose a single HTTP endpoint of your choice that receives all the information required to calculate a compliant digital signature, and it should return a DER PKCS#7 container that can be set as a value of the digital signature field.

For example, let’s say you want to sign a document with the ID my-document-id via the Server API:

POST http://localhost:5000/api/documents/my-document-id/sign
Authorization: Token token="<secret token>"
Content-Type: application/json

{
  "signingToken" : "custom-token"
}

The request accepts an optional signingToken string parameter, which will be forwarded to the signing service in the exact same shape.

You can use it to pass a token that can be used to verify the authenticity of the signing request or to provide identity information about the user applying the signature.

The signing endpoint will receive a request with the following schema:

POST http://signing-server:6000/sign
Content-Type: application/json

{
  "encoded_contents" : "CkVudW1lcmF0aW5nIG9iamVjdHM6IDExLCBkb25lLgpDb3VudGluZyBvYmplY3RzOiAxMDAlICg...",
  "digest" : "aab7fe5d814e7e8048275d19693435013727ee8002b85ba8edc29321fc2edfc9",
  "signing_token" : "custom-token"
}

In the example above, we assume that the signing service can be accessed at http://signing-server:6000/sign.

The endpoint will receive a JSON-encoded POST request containing:

  • The Base64-encoded contents of the file to sign. This represents the portion of the PDF document that is covered by the digital signature, minus the byte range that will contain the signature itself. Note that since it is Base64 encoded, you will need to decode it before signing it.

  • The digest for the contents to be signed (with the hash calculated before the contents are encoded to Base64). If your language and encryption libraries support it, you can perform the signature operation using the hash as the signature contents. In such a case, please make sure you configure PSPDFKit Server to use at least sha256 as its hashing algorithm.

  • The signing token, forwarded from the previous step.

For more details, you can look at our signing service reference implementation on GitHub.

We recommend setting up the signing service as a container on the same network as PSPDFKit Server and without external network access to guarantee fast, consistent performance and better security.

Once the signing service is up and running, you can configure PSPDFKit Server to use it by setting the SIGNING_SERVICE_URL to the signing service endpoint, e.g. http://signing-service:6000/sign. For more information on configuration and customization, you can look at our configuration guide.

Apply Signature

To digitally sign a document, you need to perform a call to the PSPDFKit.Instance#signDocument method. As its first argument, you can optionally pass an object with a placeholderSize property that can be used to override the default size that is reserved for the signature during the signing preparation of the document for the PKCS#7 container.

As a second argument, you can optionally specify an object with the signingToken string property that was described in the previous subsection. Please see the API documentation for more details.

instance
  .signDocument(null, {
    signingToken: "user-1-with-rights"
  })
  .then(() => {
    console.log("document signed.");
  })
  .catch((error) => {
    console.error("The document could not be signed.", error);
  });