Digital Signatures

Digital signatures are used to authenticate the content of PDF files and of users. When a digital signature is applied, it stores the state of the document at the signing time as well as information about the signer. The signer is identified by their X.509 certificate and private key.

How Signing Works

The digital signature and associated information is stored in a signature dictionary. And that signature dictionary is the value of a signature form field.

So, to sign a PDF document there must either be an existing signature field to fill in or a new one must be created. Since a signature is associated with a form field, it is possible to visually represent the signature with an appearance stream.

When a PDF file is signed, the signature dictionary is filled with all the necessary information, like the signing time, the signing reason or whether the signed document should allow further changes.

Then the PDF file is written but it is still incomplete. If there are no existing signatures, one has the choice to either save the complete file or just append a new revision by using the incremental writing feature. Once there are existing signatures only incremental writing may be done because otherwise the existing signatures would be invalidated.

The written file is analysed and the offset and length of the placeholder for the actual cryptographic signature is determined. This information is filled back into the written file into the appropriate field of the signature dictionary.

Afterwards a digest over the whole file minus the placeholder is taken and this digest is then signed with the provided certificate and private key (or handled via an external signing provider). The resulting signature structure is embedded into the written file and now the PDF file is properly finished.

Note: Since the PDF document needs to be written to be properly signed, no changes to it can be made afterwards. So to work with the signed document, one has to load it again.

CMS and PAdES Signatures

The PDF 2.0 specification defines three types of supported digital signatures:

With PDF 2.0 the PKCS#1 signatures are deprecated and therefore creating such signatures is not supported by HexaPDF. While CMS signatures have been supported by PDF for a long time, PAdES signatures are new with PDF 2.0 (although they have been possible with PDF 1.7 through the use of extensions).

HexaPDF supports both old-style CMS as well as the newer PAdES signatures through HexaPDF::DigitalSignature::Signing::DefaultHandler. By default CMS signatures are used for backwards-compatibility and PAdES signatures are created when explicitly requested.

PAdES signatures are more complicated and have more features/requirements. They support two different profiles called PAdES-BES (Basic Electronic Signature) and PAdES-EPES (Explicit Policy Electronic Signature). HexaPDF supports part of the PAdES-BES profile which has four levels of baseline signatures called B-B, B-T, B-LT, and B-LTA. Of these four levels B-B and B-T are currently supported where the difference between B-B and B-T is that a timestamp signature is incorporated for B-T.

While the PDF specification supports creating CMS digital signatures for certificates using the RSA, DSA and ECDSA algorithms, HexaPDF currently only supports certificates using the RSA algorithm.

Signing Modes

As suggested above there are basically two ways for creating the CMS signatures needed when signing a PDF file (customizable through HexaPDF::DigitalSignature::Signing::DefaultHandler#external_signing):

Internal Signing

If all the signing certificate and private key are available to HexaPDF, HexaPDF can create the CMS signature itself without relying on anything external. This is the preferred way if is possible.

External Signing

If the private key is not available to HexaPDF, it cannot (completely) create the CMS signature and has to rely on an external mechanism for signing and/or creating the CMS signature.

Depending on whether the signing certificate is available the signing happens differently:

  • If the certificate is available, the CMS signature object itself is created by HexaPDF and only the signing itself is done by the external mechanism. This is useful, for example, if the signing key is only available through a HSM.

  • If the certificate is not available, the external mechanism has to create the complete CMS signature. This is useful for creating CMS signatures that HexaPDF is not yet able to create itself, e.g. when a certificate uses an unsupported algorithm.

Asynchrounous Signing

This is a variant of external signing where the CMS signature creation happens at a different time than the creation of the prepared PDF file itself. In such a case the PDF is “finished” with a dummy CMS signature and the correct one later replaces the dummy one.

Types of PDF Signatures

The PDF specifications also defines different types of signatures:

Restricting Changes to a Signed Document

When using a certification signature, it is possible to restrict the changes so that any unallowed change invalidates the signature.

This is done via the so called DocMDP transformation method set on the signature dictionary which defines a set of permissions that have to be followed. The following three types of permissions are available:

  1. No changes.
  2. Filling in forms and signing.
  3. Filling in forms, signing and annotation creation, deletion and modification.

Preventing any modifications with option 1 is useful, for example, when creating signed invoices. Any and all modifications will be flagged by PDF readers.

Option 2 is the default if nothing is specified and allows users to fill out the form fields and sign the PDF.

Option 3 allows in addition to the permitted changes of option 2 the creation, deletion and modification of annotations. Note that in this case. it might be possible to change the document in a way that it still validates but misleads the recipients (see the PDF Insecurity website).

The default signing handler supports this feature via the HexaPDF::DigitalSignature::Signing::DefaultHandler#doc_mdp_permissions= method.