Skip to content
Validating PAdES Signatures

Validating PAdES Signatures

Validation runs off the reader: you hand ValidateSignatures a list of handlers, it finds every signature field in the document, matches each one to the first handler that claims it, and returns a result per field.

padesHandler, err := sighandler.NewEtsiPAdESLevelB(nil, nil, nil)
if err != nil {
    return err
}

res, err := reader.ValidateSignatures([]model.SignatureHandler{padesHandler})
if err != nil {
    return err
}

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

Both the key and the certificate are nil, because validation reads the certificate out of the signature itself.

One handler for every PAdES level

The PAdES handler does not branch on its level when validating. It claims any signature whose filter is Adobe.PPKLite and whose subfilter is ETSI.CAdES.detached, then verifies the CMS structure the same way regardless of whether the file was signed at B-B, B-T or B-LT. So a single NewEtsiPAdESLevelB(nil, nil, nil) handler covers all three.

Do not reach for NewEtsiPAdESLevelLT to do this. That constructor dereferences its appender argument, so passing nil panics rather than returning an error. Use the Level B or Level T constructor when the handler is only going to validate.

Document timestamps are a separate case. They carry the subfilter ETSI.RFC3161, which the PAdES handler does not claim, so validating a B-LTA file needs both handlers in the list:

tsHandler, err := sighandler.NewDocTimeStamp("", 0)
if err != nil {
    return err
}

handlers := []model.SignatureHandler{padesHandler, tsHandler}

An empty server URL and a zero hash are fine here; both are only used when signing. A signature with no matching handler still appears in the results, with handler not set in its Errors slice.

Reading the result

item.String() prints a readable summary, and the fields behind it are worth knowing individually.

FieldMeaning
IsSignedA signature dictionary was found for the field.
IsVerifiedThe CMS signature verifies against the bytes covered by its byte range.
GeneralizedTimeThe time from the embedded timestamp token. Zero when the signature has none, which is how you tell B-B from B-T.
IsCrlFound, IsOcspFoundRevocation data was present in the signature’s Adobe revocation-info attribute.
DiffResultsSet only by the DocMDP handler. See Sign and configure DocMDP restriction.
FieldsThe form field the signature is attached to.

IsVerified is a statement about bytes, not about trust. It says the digest matches and the CMS structure checks out against the certificate embedded in the file. It says nothing about whether that certificate chains to a root you accept, and nothing about whether it has since been revoked.

IsTrusted is never set by any handler in the SDK, so the summary always reads Trusted: Untrusted certificate, even for a signature from a well-known CA. Chain building and trust decisions are left to the caller: pull the certificates with PdfSignature.GetCerts and evaluate them against your own trust store.

IsCrlFound and IsOcspFound report on the signed attribute inside the CMS, not on the document security store. A file whose validation data lives only in the DSS reports false for both, even though the data is there.

Limitations

ValidateSignatures returns nil, nil when the document has no AcroForm or no form fields. An empty result slice means there is nothing to validate, not that validation failed, which is why the example treats a zero-length result as its own error case.

Per-signature failures do not stop the walk. When a handler’s Validate returns an error, the message is appended to that result’s Errors and the remaining signatures are still processed. Check Errors as well as IsVerified.

Validation covers the byte range each signature declares. A signature that only covers part of the file still verifies as long as those bytes are intact; nothing in the result reports how much of the document the signature actually spans.

Run the example

The example opens a file, validates with a single Level B handler, and fails if the first signature is missing or does not verify. All of it is in main.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_validate_pades_b_b.go <INPUT_PDF_PATH>

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