Sign and LTV Enable in One Revision
Signing and LTV enabling normally take two revisions, because the per-signature
VRI entry is keyed on the signature contents and those are only known once the
revision has been written. LTV.EnableChain sidesteps that by writing the
certificates and revocation data into the global part of the DSS, which a
validator may use for any signature in the document. That fits in the same
revision as the signature.
One revision, EnableChain | Two revisions, EnableAll | |
|---|---|---|
| Validation data scope | Global DSS, any signature | VRI entry for the signature, plus global |
| Chain source | Only what you pass in | Extracted from the signature, plus what you pass in |
| Covered by the signature | Yes | No, needs a document timestamp to protect it |
| Revisions in the output | 1 | 2 |
Pick this form when the output has to be a single revision, or when you want the validation data itself protected by the signature. Otherwise the second-revision form produces the more precise result.
Doing it
if err = appender.Sign(1, field); err != nil {
return err
}
ltv, err := model.NewLTV(appender)
if err != nil {
return err
}
if err := ltv.EnableChain(certChain); err != nil {
return err
}
return appender.WriteToFile(outputPath)Both operations act on the same appender and only the write has to come last.
EnableChain never looks at the signature, so its position relative to Sign
makes no difference.
certChain has to start with the signing certificate. The client walks up from
chain[0], downloading any issuer it cannot find in what you passed, and stops
at the first self-signed certificate. Put the root first and the walk terminates
immediately, leaving intermediates out. The example builds the slice from the
certificate in the PKCS12 file and then appends the issuers decoded from the
optional PEM file.
Limitations
Nothing is extracted from the signature here. EnableChain passes no signature
certificates through, so an empty or nil chain fails with
ErrSignNoCertificates, “could not retrieve certificate chain”, even on a
perfectly good signature.
No VRI entry is written, so a validator has to decide which of the global certificates and responses apply to which signature. With one signature that is unambiguous; with several signed at different times it is less so.
Everything in the LTV enable guide about silently skipped OCSP and CRL requests applies here too: fetch failures are logged at debug level and the call still returns nil.
Run the example
Everything happens in main: the PKCS12 file is decoded, the chain assembled,
the document signed with sighandler.NewAdobePKCS7Detached, and the DSS filled
in before the single write.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures/ltv
go run pdf_sign_ltv_one_revision.go cert.p12 password input.pdf output.pdf [extra_certs.pem]If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.