Skip to content
Validate a Timestamp Signature

Validate a Timestamp Signature

Validating a document timestamp goes through the same call as any other signature, PdfReader.ValidateSignatures, with a DocTimeStamp handler in the list. Both the server URL and the hash algorithm can be left empty for validation, since validation does not use the URL and reads the hash algorithm from the token:

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

A timestamped file also holds the signature the timestamp was added over, so the example passes this handler in the same list as the signature handlers. Validating Digital Signature shows the full list. The DocTimeStamp handler claims only the ETSI.RFC3161 subfilter, so where it sits in the list makes no difference.

The result carries GeneralizedTime, the time the timestamp authority put in the token, and IsVerified, which is true when the hash of the covered byte range matches the message imprint inside the token.

Picking the timestamps out of the results

ValidateSignatures returns one result per field whose value dictionary has a Type of Sig or DocTimeStamp, in document order, so a signed and timestamped file yields at least two. The example prints them all. To pick out the timestamps, go through the field each result points back at:

for i, item := range res {
    if len(item.Fields) == 0 {
        continue
    }
    d, ok := core.GetDict(item.Fields[0].V)
    if !ok {
        continue
    }
    if subFilter, _ := core.GetNameVal(d.Get("SubFilter")); subFilter == "ETSI.RFC3161" {
        fmt.Printf("Signature %d is a document timestamp from %s\n", i+1, item.GeneralizedTime)
    }
}

ETSI.RFC3161 is the subfilter every document timestamp carries, and it is also what the handler’s IsApplicable matches on when the handler is chosen.

Limitations

A handler that is not applicable to a signature is not an error. Results for the signatures nothing in the list can handle come back with IsSigned true and an Errors entry reading “handler not set”, and the call still returns nil. Pass a handler for every subfilter you care about, and check Errors rather than assuming a returned result was validated.

A validation that fails outright is not an error either. When a handler’s Validate returns an error, the message is appended to Errors, and IsSigned, IsVerified and GeneralizedTime stay at their zero values. The name, date, coverage and certificate chain are still filled in from the signature dictionary, so a corrupt timestamp prints Signed: Not signed and Signature validation: Is invalid rather than failing the call.

ValidateSignatures returns nil, nil for a document with no AcroForm or no fields. An empty slice means no signature fields, not a successful validation of none, which is why the example treats it as a failure and ends with Fail: no signature fields found.

ValidateSignatures never sets IsTrusted, and IsCrlFound and IsOcspFound are only set by the PAdES handlers. Printing a result from a DocTimeStamp handler therefore reports “Untrusted certificate”, “CRL not found” and “OCSP not found”, whatever the file actually contains. To check that the timestamp authority is one you trust, validate with ValidateSignaturesWithOpts and your roots. A document timestamp is judged at the current time unless you set ValidationTime, and its TSA certificate, and every CA above it that carries an extended key usage, must allow time stamping. See Validating Signer Trust and Revocation.

Run the example

The example validates every signature in the file and prints SignatureValidationResult.String() for each, which includes GeneralizedTime when it is set. Nothing is written; the output goes to the terminal. If the timestamp or any other signature is not signed or does not verify, the example exits with status 1 and names it, for example Fail: validation failed for signature(s) 2.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures/validation
go run pdf_sign_validate.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

Sample output

The timestamp result from a file produced by the timestamp signing example. The first result, for the signature underneath, is left out:

--- Signature 2
Name: Document Timestamp
Date: 2026-10-21 12:59:42 +0700 UTC+0700
Reason: RFC 3161 document timestamp
Location not specified
Contact info not specified
Fields: 1
Signed: Document is signed
Signature validation: Is valid
Trusted: Untrusted certificate
    no trust roots configured
Certificate: CN=www.freetsa.org,OU=TSA,O=Free TSA,L=Wuerzburg,ST=Bayern,C=DE,...
Certificate issuer: CN=www.freetsa.org,OU=Root CA,O=Free TSA,L=Wuerzburg,ST=Bayern,C=DE,...
Certificate validity: 2026-02-15T19:44:22Z - 2040-02-02T19:44:22Z
Certificate chain: 2 certificate(s)
GeneralizedTime: 2026-10-21 05:59:43 +0000 UTC
Coverage: Entire file is covered by the signature
ByteRange gap: matches /Contents
Revocation data: CRL not found
Revocation data: OCSP not found

GeneralizedTime is the time freetsa.org put in the token, in UTC. Date is the signing machine’s clock, written into the signature dictionary when the timestamp field was created.

Last updated on