Documentation
¶
Overview ¶
Package sign implements PAdES digital signatures for PDF documents per ISO 32000 §12.8 and ETSI TS 102 778.
Four conformance levels are supported:
- B-B — basic signature (CMS/PKCS#7 detached)
- B-T — adds an RFC 3161 timestamp
- B-LT — embeds revocation data (OCSP, CRL) in a Document Security Store (§12.8.6.3)
- B-LTA — adds a document-level timestamp for long-term archival
Signing uses PDF incremental updates to preserve existing content and prior signatures. CMS structures are built from scratch using encoding/asn1 — no external cryptography dependencies.
Use SignPDF with an Options containing a Signer (local key via NewLocalSigner or external HSM via NewExternalSigner), optional TSAClient and OCSPClient for higher conformance levels.
Package sign implements PAdES digital signatures for PDF documents.
The signing flow uses incremental updates (PDF appends) so existing content and any prior signatures are preserved. The CMS/PKCS#7 detached signature is built from scratch using encoding/asn1 — no external cryptography dependencies beyond the Go standard library.
PAdES conformance levels:
- B-B: basic signature (always)
- B-T: adds a trusted timestamp from an RFC 3161 TSA
- B-LT: embeds revocation data via a Document Security Store
- B-LTA: adds a document timestamp for archival
Index ¶
- func AddDSS(pdfBytes []byte, dss *DSS) ([]byte, error)
- func AddDocumentTimestamp(pdfBytes []byte, tsaClient *TSAClient, hashFunc crypto.Hash) ([]byte, error)
- func CollectValidationData(chain []*x509.Certificate, ocspClient *OCSPClient) (ocspResponses [][]byte, err error)
- func SignPDF(pdfBytes []byte, opts Options) ([]byte, error)
- type Algorithm
- type ChainStatus
- type DSS
- type ExternalSigner
- type LocalSigner
- type OCSPClient
- type Options
- type PAdESLevel
- type Report
- type SignatureResult
- type Signer
- type TSAClient
- type VRIEntry
- type VerifyOptions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddDSS ¶
AddDSS adds a Document Security Store to the PDF via an incremental update. This is used to embed validation data for PAdES B-LT.
func AddDocumentTimestamp ¶
func AddDocumentTimestamp(pdfBytes []byte, tsaClient *TSAClient, hashFunc crypto.Hash) ([]byte, error)
AddDocumentTimestamp appends a document timestamp signature to a signed PDF. This upgrades a B-LT signature to B-LTA by proving the DSS data existed at the timestamp time.
The document timestamp covers the entire PDF (including DSS), uses /SubFilter /ETSI.RFC3161, and /Type /DocTimeStamp.
func CollectValidationData ¶
func CollectValidationData(chain []*x509.Certificate, ocspClient *OCSPClient) (ocspResponses [][]byte, err error)
CollectValidationData gathers certificates, OCSP responses, and CRLs for the given signer's certificate chain. This is a convenience that uses OCSPClient to fetch revocation data.
Types ¶
type Algorithm ¶
type Algorithm int
Algorithm identifies a hash + signature algorithm pair.
const ( // SHA256WithRSA is RSA PKCS#1 v1.5 with SHA-256. SHA256WithRSA Algorithm = iota // SHA384WithRSA is RSA PKCS#1 v1.5 with SHA-384. SHA384WithRSA // SHA512WithRSA is RSA PKCS#1 v1.5 with SHA-512. SHA512WithRSA // SHA256WithECDSA is ECDSA with SHA-256. SHA256WithECDSA // SHA384WithECDSA is ECDSA with SHA-384. SHA384WithECDSA // SHA512WithECDSA is ECDSA with SHA-512. SHA512WithECDSA )
func (Algorithm) DigestOID ¶
func (a Algorithm) DigestOID() asn1.ObjectIdentifier
DigestOID returns the ASN.1 OID for the hash algorithm.
func (Algorithm) SignatureOID ¶
func (a Algorithm) SignatureOID() asn1.ObjectIdentifier
SignatureOID returns the ASN.1 OID for the signature algorithm.
type ChainStatus ¶ added in v0.10.0
type ChainStatus int
ChainStatus reports the result of validating a signer certificate chain against caller-supplied trust anchors.
const ( // ChainStatusNoRootsSupplied means VerifyOptions.Roots was nil, so no // chain was built. The crypto checks (DigestValid, SignatureValid) are // unaffected by this. ChainStatusNoRootsSupplied ChainStatus = iota // ChainStatusTrusted means the signer certificate chains to one of the // supplied roots. ChainStatusTrusted // ChainStatusUntrusted means chain building failed for a reason other // than expiry — most commonly, no path to a trusted root. ChainStatusUntrusted // ChainStatusExpired means the signer certificate was outside its // validity period at the validation time. ChainStatusExpired )
func (ChainStatus) String ¶ added in v0.10.0
func (s ChainStatus) String() string
String returns the ETSI-style status name used in Report output.
type DSS ¶
type DSS struct {
Certs [][]byte // DER-encoded certificates
OCSPs [][]byte // DER-encoded OCSP responses
CRLs [][]byte // DER-encoded CRLs
// VRI maps signature Contents SHA-1 hex keys to per-signature validation data.
VRI map[string]*VRIEntry
}
DSS (Document Security Store) holds validation data for long-term signature verification (PAdES B-LT / B-LTA). It is embedded in the catalog dictionary per ETSI EN 319 142-1.
Structure:
/DSS <<
/Certs [stream ...] % all certificates
/OCSPs [stream ...] % all OCSP responses
/CRLs [stream ...] % all CRLs
/VRI << % per-signature validation data
/AABBCCDD... << % key = uppercase hex SHA-1 of sig /Contents
/Cert [stream ...]
/OCSP [stream ...]
/CRL [stream ...]
>>
>>
>>
func (*DSS) AddSignatureValidation ¶
func (d *DSS) AddSignatureValidation(sigContents []byte, chain []*x509.Certificate, ocspResponses, crls [][]byte)
AddSignatureValidation collects validation data for a signature and adds it to the DSS. The sigContents is the raw signature bytes from /Contents. The chain is the signer's certificate chain. OCSP responses and CRLs are optional (pass nil to skip).
func (*DSS) Build ¶
func (d *DSS) Build(addObject func(core.PdfObject) *core.PdfIndirectReference) *core.PdfDictionary
Build creates the DSS dictionary and all referenced stream objects. The addObject callback assigns object numbers to each stream and returns indirect references for embedding in the dictionary.
type ExternalSigner ¶
type ExternalSigner struct {
// contains filtered or unexported fields
}
ExternalSigner delegates signing to an external function (HSM, KMS, etc.).
func NewExternalSigner ¶
func NewExternalSigner( signFn func(digest []byte) ([]byte, error), certs []*x509.Certificate, algo Algorithm, ) (*ExternalSigner, error)
NewExternalSigner creates a signer that delegates to the given function. The function receives a hash digest and must return raw signature bytes.
func (*ExternalSigner) Algorithm ¶
func (s *ExternalSigner) Algorithm() Algorithm
Algorithm returns the signature algorithm.
func (*ExternalSigner) CertificateChain ¶
func (s *ExternalSigner) CertificateChain() []*x509.Certificate
CertificateChain returns the signer's certificate chain.
type LocalSigner ¶
type LocalSigner struct {
// contains filtered or unexported fields
}
LocalSigner signs using a local private key.
func NewLocalSigner ¶
func NewLocalSigner(key crypto.Signer, certs []*x509.Certificate) (*LocalSigner, error)
NewLocalSigner creates a signer from a local private key and certificate chain. The algorithm is auto-detected from the key type (RSA or ECDSA) with SHA-256.
func ParsePKCS12 ¶ added in v0.6.0
func ParsePKCS12(data []byte, password string) (*LocalSigner, error)
ParsePKCS12 parses a PKCS#12 (.p12/.pfx) archive from bytes and returns a LocalSigner.
PKCS#12 decoding is not implemented: it would require golang.org/x/crypto/pkcs12. Callers should parse the archive themselves and use NewLocalSigner with the pre-parsed key and certificate.
func (*LocalSigner) Algorithm ¶
func (s *LocalSigner) Algorithm() Algorithm
Algorithm returns the signature algorithm.
func (*LocalSigner) CertificateChain ¶
func (s *LocalSigner) CertificateChain() []*x509.Certificate
CertificateChain returns the signer's certificate chain.
func (*LocalSigner) SetAlgorithm ¶
func (s *LocalSigner) SetAlgorithm(algo Algorithm)
SetAlgorithm overrides the auto-detected algorithm.
type OCSPClient ¶
type OCSPClient struct {
// HTTPClient is the HTTP client to use. If nil, http.DefaultClient is used.
HTTPClient *http.Client
}
OCSPClient fetches OCSP responses for certificate revocation checking.
func (*OCSPClient) FetchChainResponses ¶
func (c *OCSPClient) FetchChainResponses(chain []*x509.Certificate) ([][]byte, error)
FetchChainResponses fetches OCSP responses for all certificates in the chain (except the root/self-signed). Returns responses in chain order. Certificates without OCSP responder URLs are skipped.
func (*OCSPClient) FetchResponse ¶
func (c *OCSPClient) FetchResponse(cert, issuer *x509.Certificate) ([]byte, error)
FetchResponse sends an OCSP request for the given certificate and returns the DER-encoded OCSP response. The issuer certificate is required to build the request.
type Options ¶
type Options struct {
// Signer performs the cryptographic signing.
Signer Signer
// Level is the PAdES conformance level (default: LevelBB).
Level PAdESLevel
// Name is the signer's name (optional, shown in PDF viewer).
Name string
// Reason is the reason for signing (optional).
Reason string
// Location is the signing location (optional).
Location string
// ContactInfo is the signer's contact info (optional).
ContactInfo string
// SigningTime overrides the signing timestamp. If zero, time.Now() is used.
SigningTime time.Time
// TSAClient is the RFC 3161 TSA client (required for B-T and above).
TSAClient *TSAClient
// OCSPClient fetches OCSP responses (optional, used for B-LT and above).
// If nil and Level >= LevelBLT, OCSP data is omitted from the DSS.
OCSPClient *OCSPClient
// CRLs provides pre-fetched CRL data (DER-encoded) for embedding in
// the DSS. Optional alternative to OCSP for revocation data.
CRLs [][]byte
// ExtraCerts provides additional certificates to embed in the DSS
// (e.g., intermediate CAs not in the signer's chain).
ExtraCerts [][]byte
}
Options configures the signing operation.
type PAdESLevel ¶
type PAdESLevel int
PAdESLevel specifies the PAdES conformance level.
const ( // LevelBB is PAdES B-B (basic signature with ESS signing-certificate-v2). LevelBB PAdESLevel = iota // LevelBT is PAdES B-T (B-B + RFC 3161 timestamp). LevelBT // LevelBLT is PAdES B-LT (B-T + embedded revocation data in DSS). LevelBLT // LevelBLTA is PAdES B-LTA (B-LT + document timestamp). LevelBLTA )
type Report ¶ added in v0.10.0
type Report struct {
// Signatures holds one SignatureResult per signature found in the PDF,
// in the order they appear in the file.
Signatures []SignatureResult
}
Report is the result of Verify.
func Verify ¶ added in v0.10.0
func Verify(pdfBytes []byte, opts VerifyOptions) (*Report, error)
Verify checks every signature in a signed PDF and reports what it found. It performs OFFLINE checks only: it locates each signature dictionary, verifies the /ByteRange digest against the CMS messageDigest attribute, verifies the CMS signature over the signed attributes, and — when VerifyOptions.Roots is supplied — builds a certificate chain.
Callers should generally check SignatureResult.OK(), the single aggregate verdict, rather than inspecting individual fields.
Verify does not fetch revocation data (OCSP/CRL), does not validate any embedded RFC 3161 timestamp token or /DSS contents beyond their presence, and does not classify the PAdES conformance level of what it found.
pdfBytes must be the exact bytes produced by SignPDF (or an equivalent signer) — Verify computes ByteRange offsets against pdfBytes directly, so re-encoding, trimming, or otherwise altering the file before calling Verify will misreport or fail the digest check. It returns an error only when the input cannot be parsed as a signed PDF at all (no signature found, malformed CMS); tamper and trust failures are reported through SignatureResult fields, never as an error.
type SignatureResult ¶ added in v0.10.0
type SignatureResult struct {
// Name, Reason, Location come from the signature dictionary, as set by
// Options.Name/Reason/Location at signing time.
Name, Reason, Location string
// SignerCertificate is the certificate embedded in the CMS SignedData
// that matches the SignerInfo's SignerIdentifier (issuer + serial
// number) — the certificate the signature was checked against.
SignerCertificate *x509.Certificate
// SigningTime is the CMS signingTime signed attribute.
SigningTime time.Time
// DigestValid is true when the CMS messageDigest signed attribute
// equals the hash of the bytes covered by /ByteRange.
DigestValid bool
// SignatureValid is true when the CMS signature cryptographically
// verifies against SignerCertificate's public key over the DER
// encoding of the CMS signed attributes.
SignatureValid bool
// ByteRangeCoversFile is true when /ByteRange spans the entire file
// with no gap other than the /Contents hex string itself.
ByteRangeCoversFile bool
// ChainStatus is the result of building a chain from
// SignerCertificate to VerifyOptions.Roots.
ChainStatus ChainStatus
// HasTimestamp reports whether an RFC 3161 timestamp token is present
// as a CMS unsigned attribute. Its presence is reported, but the token
// itself is not cryptographically validated.
HasTimestamp bool
// HasDSS reports whether the document catalog has a /DSS entry —
// catalog-derived (via the parsed object graph) when the file parses,
// falling back to a raw "/DSS" byte scan otherwise. Its contents (OCSP
// responses, CRLs, certificates) are not validated.
HasDSS bool
// IsDocTimeStamp is true when this entry is a document timestamp
// (added by AddDocumentTimestamp) rather than an author or approval
// signature: its /V dictionary has /Type /DocTimeStamp or /SubFilter
// /ETSI.RFC3161. Both share the AcroForm /FT /Sig field type, so
// locateSignaturesGraph reports both kinds; callers that only care
// about author/approval signatures should filter on this field. The
// /Contents of a document timestamp holds an RFC 3161 token rather
// than a CMS SignedData signing this document's ByteRange, so
// DigestValid, SignatureValid, and ChainStatus are not meaningful
// verdicts on the document's authorship for these entries.
IsDocTimeStamp bool
}
SignatureResult reports what Verify found and checked for one signature in the PDF. A field being false never means "not checked" versus "checked and failed" — see the doc comment on each field for what it covers.
func (SignatureResult) OK ¶ added in v0.10.1
func (r SignatureResult) OK() bool
OK reports the aggregate verdict for this signature: the ByteRange digest matched, the CMS signature verified, /ByteRange covers the entire file (no unsigned trailing bytes — the classic incremental-update tamper), and the signer chains to a caller-supplied trust root. It is false when VerifyOptions.Roots was nil: an aggregate verdict without trust anchors would be meaningless. Callers that intentionally skip chain validation should check the individual fields instead.
type Signer ¶
type Signer interface {
// Sign signs the given digest and returns the raw signature bytes.
Sign(digest []byte) ([]byte, error)
// Algorithm returns the signature algorithm.
Algorithm() Algorithm
// CertificateChain returns the signer's certificate chain.
// The first certificate is the signing certificate.
CertificateChain() []*x509.Certificate
}
Signer performs the cryptographic signing operation.
type TSAClient ¶
type TSAClient struct {
// URL is the TSA endpoint (e.g., "http://timestamp.digicert.com").
URL string
// HTTPClient is the HTTP client to use. If nil, http.DefaultClient is used.
HTTPClient *http.Client
}
TSAClient is an RFC 3161 Time-Stamp Authority client. It sends a TimeStampReq to the TSA URL and returns the timestamp token.
func NewTSAClient ¶
NewTSAClient creates a TSA client for the given URL.
type VerifyOptions ¶ added in v0.10.0
type VerifyOptions struct {
// Roots are the caller-supplied trust anchors used to build a chain for
// each signer certificate. Nil skips chain validation entirely; every
// SignatureResult.ChainStatus is then ChainStatusNoRootsSupplied.
Roots *x509.CertPool
// At is the time used for certificate validity checks. The zero value
// uses each signature's own SigningTime.
At time.Time
}
VerifyOptions configures Verify.