Skip to content
Validating Digital Signature

Validating Digital Signature

PdfReader.ValidateSignatures walks the document’s signature fields, recomputes the digest over each signature’s byte ranges, and returns one result per signature. You supply the handlers it may use; a handler with nil key and certificate is a validation-only handler, since the certificate needed comes out of the signature itself.

Validating

handlerX509RSASHA1, err := sighandler.NewAdobeX509RSASHA1(nil, nil)
if err != nil {
    return err
}

handlerPKCS7Detached, err := sighandler.NewAdobePKCS7Detached(nil, nil)
if err != nil {
    return err
}

res, err := reader.ValidateSignatures([]model.SignatureHandler{
    handlerX509RSASHA1,
    handlerPKCS7Detached,
})
if err != nil {
    return err
}
if len(res) == 0 {
    return errors.New("no signature fields found")
}

for i, item := range res {
    fmt.Printf("--- Signature %d\n%s\n", i+1, item.String())
}

Handlers are matched by /Filter and /SubFilter and the list is scanned in order, first match wins. That matters when a handler wraps another: put the more specific one first, or the general one will claim the signature. A DocMDP-certified signature, for instance, is also a valid adbe.pkcs7.detached signature, so a DocMDPHandler placed after a plain PKCS#7 handler never gets a chance to check the document’s modifications.

Reading the result

FieldMeaning
IsSignedThe field has a signature dictionary. Set even when validation fails.
IsVerifiedThe digest matched and the signature is cryptographically sound.
IsTrustedWhether the certificate chains to a trusted root. Never set by the bundled handlers.
ErrorsWhy validation failed, as strings.
FieldsThe signature field the result belongs to.
DiffResultsModification analysis, present only for DocMDP validation.
IsCrlFound, IsOcspFoundRevocation data found in the signature. Only the PAdES handler sets these.

The one to check is IsVerified. Because IsTrusted is never set, String() always prints Trusted: Untrusted certificate and, outside PAdES, always prints that no CRL or OCSP data was found. Neither line says anything about the signature.

Certificate trust is left to the caller. Reach the signature through the result’s field, pull the chain out of it, and verify that against your own root pool:

if sigField, ok := res[0].Fields[0].GetContext().(*model.PdfFieldSignature); ok {
    certs, err := sigField.V.GetCerts()
    // ... verify certs[0] against an x509.CertPool.
}

Limitations

A document without an AcroForm, or with an AcroForm holding no fields, gets nil, nil back: no error and no results. That is why the example checks the length separately from the error.

Validation failures do not surface as the returned error. They land in result.Errors on the individual result, with IsVerified false. The returned error is reserved for problems reading the file. If no handler in your list is applicable, the result comes back with IsSigned true, IsVerified false, and handler not set in Errors, which looks like a broken signature but means an unsupported signature type.

There is no flag for whether a signature covers the whole file. A signature over an early revision of an incrementally updated document verifies cleanly for the bytes it covers, while later revisions changed the rest. Comparing revisions is the DocMDP handler’s job, so validating a certified document means wrapping the base handler in sighandler.NewDocMDPHandler.

Timestamp tokens and PAdES signatures need their own handlers. See Validating Timestamp in Digital Signature and Validating PAdES Signatures.

Unlike signing, validation does not need a seekable source. It re-reads the signed byte ranges through the parser, so a reader built with model.NewPdfReaderFromParser can validate signatures even though it cannot be passed to NewPdfAppender.

Run the example

The example prints the String() form of every result, which covers the name, date, reason, location and the flags above.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_validate.go <IN.pdf>

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on