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
| Field | Meaning |
|---|---|
IsSigned | The field has a signature dictionary. Set even when validation fails. |
IsVerified | The digest matched and the signature is cryptographically sound. |
IsTrusted | Whether the certificate chains to a trusted root. Never set by the bundled handlers. |
Errors | Why validation failed, as strings. |
Fields | The signature field the result belongs to. |
DiffResults | Modification analysis, present only for DocMDP validation. |
IsCrlFound, IsOcspFound | Revocation 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.