Help us shape the future of Go! Take the 2026 Go Developer Survey

sign

package
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddDSS

func AddDSS(pdfBytes []byte, dss *DSS) ([]byte, error)

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.

func SignPDF

func SignPDF(pdfBytes []byte, opts Options) ([]byte, error)

SignPDF applies a PAdES digital signature to an existing PDF. It returns the signed PDF bytes (original + incremental update).

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) HashFunc

func (a Algorithm) HashFunc() crypto.Hash

HashFunc returns the crypto.Hash for this 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 NewDSS

func NewDSS() *DSS

NewDSS creates an empty DSS.

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.

func (*ExternalSigner) Sign

func (s *ExternalSigner) Sign(digest []byte) ([]byte, error)

Sign delegates signing of the given digest to the external function.

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.

func (*LocalSigner) Sign

func (s *LocalSigner) Sign(digest []byte) ([]byte, error)

Sign signs the given digest using the local private key.

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 NewOCSPClient

func NewOCSPClient() *OCSPClient

NewOCSPClient creates a new OCSP client.

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

func NewTSAClient(url string) *TSAClient

NewTSAClient creates a TSA client for the given URL.

func (*TSAClient) Timestamp

func (c *TSAClient) Timestamp(digest []byte, hashFunc crypto.Hash) ([]byte, error)

Timestamp sends a timestamp request and returns the DER-encoded timestamp token. The data parameter is the digest to timestamp, hashFunc identifies the hash algorithm.

type VRIEntry

type VRIEntry struct {
	Certs [][]byte
	OCSPs [][]byte
	CRLs  [][]byte
}

VRIEntry holds validation data for a single signature.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL