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 foundGeneralizedTime 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.