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, Enable per signature | |
|---|---|---|
| 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
signing certificate in the PKCS12 file, then the CA certificates bundled in the
same file, then any issuers from the optional PEM file that are not already in
the slice.
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 with
pkcs12.DecodeChain from software.sslmate.com/src/go-pkcs12, the chain
assembled, the document signed with sighandler.NewAdobePKCS7Detached, and the
DSS filled in before the single write. The key in the PKCS12 file has to be RSA;
any other key type stops the example with an error naming the type. The
Running the examples
section of the LTV README shows how to check the output offline.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures/ltv
go run pdf_sign_ltv_one_revision.go <FILE.p12> <P12_PASS> <INPUT_PDF_PATH> <OUTPUT_PDF_PATH> [<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.