Signing
Signing a PDF with UniPDF always goes through model.PdfAppender. The appender
writes an incremental update over the original bytes instead of rewriting the
file, so earlier revisions survive and a signature that was already on the
document keeps validating. One consequence catches people early: the reader you
build the appender from needs a seekable source. model.NewPdfAppender returns
an error unless the reader was created from something that implements both
io.ReadSeeker and io.ReaderAt, such as an *os.File or a *bytes.Reader.
The handler you pass to model.NewPdfSignature decides the signature format.
sighandler.NewAdobePKCS7Detached covers ordinary PKCS#7 signing. The
NewEtsiPAdES* constructors produce PAdES signatures, which are graded by level.
| Level | Constructor | What it adds over the level above |
|---|---|---|
| B-B | NewEtsiPAdESLevelB | The signature itself, covering the document. |
| B-T | NewEtsiPAdESLevelT | A trusted timestamp from a TSA, evidence of when the signature existed. |
| B-LT | NewEtsiPAdESLevelLT | Certificates, CRLs and OCSP responses, written into the document security store so the signature can still be validated once those are no longer reachable online. |
| B-LTA | NewEtsiPAdESLevelLT, then NewDocTimeStamp | A document timestamp over the whole file, which extends the life of the validation data. |
Each level is the one before it plus the extra material, so B-LT is also timestamped and B-T is also a valid B-B signature.
B-LTA is the odd one. sighandler.EtsiPAdESLevel defines only LevelB,
LevelT and LevelLT, so there’s no constructor to ask for it. You reach B-LTA
by signing at B-LT, writing the result, then opening that output with a fresh
reader and appender and adding a second signature built from
sighandler.NewDocTimeStamp. The pdf_sign_pades_b_lta.go example does exactly
that, with an intermediate pass that carries the DSS across via
appender.GetDSS and appender2.SetDSS.
Level LT also needs the appender itself. NewEtsiPAdESLevelLT takes one as its
last argument, and initializing the signature fails with “appender must not be
nil for Level LT signature handler” if it’s missing. ECDSA keys have their own
constructors (NewEtsiPAdESLevelBEcdsa and friends) and require PDF 2.0 or
later.
The visible mark on the page is a separate concern from the cryptography.
annotator.NewSignatureField builds the appearance, and passing a rectangle of
zeros gives you a valid signature with nothing drawn.
Worked examples for each of these live in the signature guides.