General API design

PyHanko’s validation functionality resides in the validation module. Its most important components are

While you probably won’t need to interface with DocumentSecurityStore directly, knowing a little about EmbeddedPdfSignature and SignatureStatus is useful.

Accessing signatures in a document

There is a convenience property on PdfFileReader, aptly named embedded_signatures. This property produces an array of EmbeddedPdfSignature objects, in the order that they were applied to the document. The result is cached on the reader object.

These objects can be used to inspect the signature manually, if necessary, but they are mainly intended to be used as input for validation APIs.

Validating a PDF signature

All validation in pyHanko is done with respect to a certain validation context (an object of type pyhanko_certvalidator.ValidationContext). This object tells pyHanko what the trusted certificates are, and transparently provides mechanisms to request and keep track of revocation data. For LTV validation purposes, a ValidationContext can also specify a point in time at which the validation should be carried out.

Originally, the principal purpose of the ValidationContext was to let the user explicitly specify their own trust settings, but ValidationContext objects are stateful: they also accumulate revocation data and validation results. It may be necessary to juggle several different validation contexts over the course of a validation operation. For example, when performing LTV validation, pyHanko will first validate the signature’s timestamp against the user-specified validation context, and then build a new validation context relative to the signing time specified in the timestamp.

Note

For a more systematic and declarative approach to validation that abstracts away the ValidationContext juggling, see The AdES validation engine.

Here’s a simple example to illustrate the process of validating a PDF signature w.r.t. a specific trust root.

from pyhanko.keys import load_cert_from_pemder
from pyhanko_certvalidator import ValidationContext
from pyhanko.pdf_utils.reader import PdfFileReader
from pyhanko.sign.validation import validate_pdf_signature

root_cert = load_cert_from_pemder('path/to/certfile')
vc = ValidationContext(trust_roots=[root_cert])

with open('document.pdf', 'rb') as doc:
    r = PdfFileReader(doc)
    sig = r.embedded_signatures[0]
    status = validate_pdf_signature(sig, vc)
    print(status.pretty_print_details())

Deprecation of OS trust list

Warning

Specifying trust_roots (or a trust_manager) is optional today: if you leave it out, the ValidationContext falls back to the operating system’s trust list. This is for historical reasons: it’s a behaviour that stems from the library that pyhanko-certvalidator was forked from.

This fallback is deprecated and will be removed in a future pyhanko-certvalidator release, at which point trust roots will have to be supplied explicitly. The extra_trust_roots parameter, which exists to supplement the platform’s trust list, is deprecated along with it.

The reason is that the platform trust list is maintained for TLS purposes, which makes it a nonsensical source of trust for document signature validation.

If you do want to keep validating against a set of TLS roots for one reason or another, load them explicitly from a PEM bundle:

from pyhanko.keys import load_certs_from_pemder

vc = ValidationContext(
    trust_roots=list(
        load_certs_from_pemder(['/path/to/ca-bundle.pem'])
    )
)