Documentation
¶
Overview ¶
Package xmlsig provides a fluent builder API for creating and validating XML Digital Signatures (XML-DSIG) per the W3C specification.
Overview ¶
XML-DSIG attaches cryptographic signatures to XML documents. This library supports three signature shapes:
- Enveloped: the Signature is a child of the signed element. (The element signs itself minus the Signature node.)
- Enveloping: the Signature wraps the signed data inside <Object> children.
- Detached: the Signature references external resources by URI.
Getting Started — Signing ¶
// Prepare crypto materials
dm := crypto.NewStdXMLDigestModule()
signerMod, err := crypto.NewStdXMLSignatureSignerModule(privKey, crypto.SHA256)
if err != nil {
log.Fatal(err)
}
// Build the signature structure
b := xmlsig.NewXMLSignatureBuilder(dm).
WithParent(doc.Root(), []string{xmlsig.SignEntireElement}).
WithCerts([][]byte{certDER}).
WithKeyName("invoice-server")
// Produce the signed XML
signedEl, err := b.BuildSignature(signerMod)
if err != nil {
log.Fatal(err)
}
Getting Started — Validation ¶
// Prepare a trust store and validation modules
certPool := []*x509.Certificate{trustedCert}
vcm := crypto.NewStdXMLStandardCryptoVerifierModuleWithNoRevocationCheck()
dm := crypto.NewStdXMLDigestModule()
vc := xmlsig.NewDefaultValidationContext(certPool, vcm, dm)
// Load and validate
doc := etree.NewDocument()
doc.ReadFromFile("signed.xml")
err = vc.Validate(doc.Root())
if err != nil {
log.Fatalf("validation failed: %v", err)
}
fmt.Println("signature verified successfully")
Key Concepts ¶
Element: any etree.Node representing an XML element (<Person>, <Invoice>, etc.). Elements are identified by their "Id" attribute (W3C ID AS). By default the library looks for the "Id" attribute; configure with WithIdAttribute.
SignedInfo: the canonicalized component containing the digest algorithms, reference URIs, transform chain, and the signature algorithm. This is what the cryptographic signature protects.
Object: an arbitrary container inside <Signature> that holds embedded data. Useful for enveloping signatures where the signed content travels WITH the signature. Each Object can reference zero or more child elements to sign.
KeyInfo: metadata describing the public key or certificate used for verification. Typically carries X509 certificates or a key name.
External Reference: a reference to data outside the signed document (by URI or in-memory byte slice). Used for detached signatures.
Architecture ¶
The package splits concerns into four sub-packages:
- canonicalizers — C14N algorithms (1.0, 1.1, exclusive, with/without comments)
- crypto — digest functions, RSA/ECDSA signers, X509 verifiers
- spec — XML-DSIG constants, URI registry, XML schema structs
- etreeutils — namespace helpers forked from russellhaering/goxmldsig
Import these sub-packages only when you need fine-grained control (custom canonicalizers, alternate hash functions, etc.). Most applications interact exclusively with the top-level xmlsig types.
Thread Safety ¶
XMLSignatureBuilder is NOT thread-safe. Construct on one goroutine, sign on another — but never share a builder instance across goroutines concurrently.
ValidationContext is designed for reuse across goroutines after construction.
Specification Support ¶
Primary: W3C REC-xmlsignature-20020212 (DSIG v1.1) Partial: W3C REC-xmlsignature-v2.0-20130401 (DSIG R2)
Index ¶
- Constants
- Variables
- type EmbeddedElement
- type ExternalRef
- type ObjectEntry
- type ResolveFn
- type ValidationContext
- type XMLSignatureBuilder
- func (b *XMLSignatureBuilder) BuildSignature(signerModule crypto.XMLSignatureSignerModule) (*etree.Element, error)
- func (b *XMLSignatureBuilder) CanonicalizeElement(el *etree.Element) ([]byte, error)
- func (b *XMLSignatureBuilder) WithCanonicalizer(canonicalizer canonicalizers.Canonicalizer) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithCerts(certsDer [][]byte)
- func (b *XMLSignatureBuilder) WithDigestMethod(digestMethod spec.XMLDigestAlgorithmID) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithEmbeddedElement(e EmbeddedElement) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithExternalRef(r ExternalRef) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithExternalReferences(uris []string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithIdAttribute(idAttribute string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithKeyInfo(keyInfo *etree.Element) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithKeyName(name string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithObject(element *etree.Element, IDsToSign []string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithObjects(objects []ObjectEntry) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithParent(parent *etree.Element, IDsToSign []string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithPrefix(prefix string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithRoot(root *etree.Element) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithSignatureId(signatureId string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithSignedPropertiesId(id string) *XMLSignatureBuilder
- func (b *XMLSignatureBuilder) WithSignedSignatureProperties(props ...*etree.Element) *XMLSignatureBuilderdeprecated
- func (b *XMLSignatureBuilder) WithUnsignedSignatureProperties(props ...*etree.Element) *XMLSignatureBuilder
Constants ¶
const SignEntireElement = "#_XMLODSIG_SIGN_ENTIRE_"
SignEntireElement is a sentinel value for IDsToSign passed to WithParent and WithObject, indicating that the entire parent/Object element should be signed as an enveloped signature.
Unlike the deprecated empty-string literal (""), this named constant cannot accidentally collide with a real element's Id attribute. The leading '#' is illegal in XML NCNames, making collisions practically impossible. Both values are accepted for backward compatibility.
Variables ¶
var ( // ErrMissingSignature indicates that no enveloped signature was found referencing // the top level element passed for signature verification. ErrMissingSignature = errors.New("missing signature referencing the top-level element") // ErrInvalidSignature indicates the Signature element does not conform to the // expected XML-DSIG shape. ErrInvalidSignature = errors.New("invalid Signature") // ErrParentElementNotFound indicates that a parent element identified by ID // could not be located during signature construction. ErrParentElementNotFound = errors.New("xmldsig: parent element not found") // ErrObjectElementNotFound indicates that an element referenced by an Object // could not be located during signature construction. ErrObjectElementNotFound = errors.New("xmldsig: object element not found") // ErrUnknownTransform indicates a transform algorithm URI is not recognised. ErrUnknownTransform = errors.New("xmldsig: unknown transform algorithm") // ErrReferencedElementNotFound indicates that a URI-based reference could not // be resolved to an element during validation. ErrReferencedElementNotFound = errors.New("xmldsig: referenced element not found") // ErrUnresolvedExternalRef indicates that an external-reference URI could not // be resolved and its content was never verified. ErrUnresolvedExternalRef = errors.New("xmldsig: external reference could not be resolved and was not verified") )
var ErrMalformedSignatureElement = errors.New("malformed Signature element")
In most places, we use etree Elements, but while deserializing the Signature, we use encoding/xml unmarshal directly to convert to a convenient go struct. This presents a problem in some cases because when an xml element repeats under the parent, the last element will win and/or be appended. We need to assert that the Signature object matches the expected shape of a Signature object. ErrMalformedSignatureElement signals that a candidate Signature element violates the XML-DSIG structural invariant: its required children either appear with the wrong multiplicity or belong to the wrong namespace.
Functions ¶
This section is empty.
Types ¶
type EmbeddedElement ¶ added in v0.3.0
type EmbeddedElement struct {
Element *etree.Element
Type string // optional @Type on the emitted ds:Reference
RefId string // optional @Id on the emitted ds:Reference
InObject bool // false => under ds:Signature; true => inside ds:Object
RefTargetId string // optional: when non-empty, the emitted
}
EmbeddedElement couples a caller-built element with the ds:Reference that binds it into SignedInfo. This is the XML-DSig pattern for signed embedded data: the element is placed in the document and referenced by URI="#<element Id>" with a no-Transforms reference — its digest is the element's canonical form in its final namespace context (C14N 1.1 document subsets), per the document's CanonicalizationMethod.
Element must carry an Id attribute (the builder's IdAttribute); the Id is the reference target and is caller-controlled (deterministic). The element must carry or inherit all namespace declarations it uses at its final position (e.g. a wrapper-root document declaring them on its root).
InObject=false places the element directly under ds:Signature (last child); InObject=true places it inside a shared, attribute-free ds:Object (BDOC shape: the Object is a pure container — no Id, no attributes — and is not itself referenced).
type ExternalRef ¶
type ExternalRef struct {
URI string
Data []byte
Type string
Id string
// Raw digests the payload as raw octets: the ds:Reference carries NO
// Transforms element and the payload is never parsed or canonicalized,
// even when it is well-formed XML (XML-DSig §4.3.3.2 / §2.1.1: with no
// Transforms the resource's content is digested directly).
Raw bool
}
ExternalRef represents a detached (standalone) reference whose payload is provided in-memory rather than fetched from a URI.
When Data is non-empty, the digest is computed directly from Data and no I/O (file read / HTTP fetch) is attempted. The URI attribute is set to the given URI (empty string is valid per XML-DSIG).
type ObjectEntry ¶
ObjectEntry groups an XML element with the subset of its children to sign.
When appended to a signature via WithObject, the Element becomes the content of a <ds:Object> child of <ds:Signature>. The IDsToSign slice controls which descendants (or the object itself) appear as <Reference> elements inside SignedInfo:
- Use SignEntireElement to sign the entire <ds:Object> as-is.
- Provide concrete element IDs (e.g., []string{"item1", "item2"}) to sign specific children.
- Avoid passing an empty string (""); it is kept for backward compatibility but treated identically to SignEntireElement.
type ResolveFn ¶
ResolveFn resolves an external reference URI to its raw byte payload. Return nil when the URI is unavailable; the caller decides whether that constitutes a validation error.
type ValidationContext ¶
type ValidationContext struct {
TrustedCertificates [][]byte
IdAttribute string
// AllowUnresolvedExternalRefs controls handling of external-reference URIs that
// cannot be resolved (no resolver configured, or the resolver returns nil).
// When false (default) validation fails with ErrUnresolvedExternalRef: the
// referenced content was never verified. When true such references are skipped.
AllowUnresolvedExternalRefs bool
ResolveExternalRef ResolveFn // optional; unresolved externals fail unless AllowUnresolvedExternalRefs is set
// contains filtered or unexported fields
}
func NewDefaultValidationContext ¶
func NewDefaultValidationContext(trustedCerts [][]byte, vm c.XMLSignatureVerifierModule, dm c.XMLDigestModule) *ValidationContext
func (*ValidationContext) Validate ¶
func (ctx *ValidationContext) Validate(el *etree.Element) error
Validate validates the signature found in el.
el must provide the signed document's full namespace context. For documents whose signatures rely on namespace declarations made on ancestor elements (BDOC shape: the wrapper root declares the namespaces and the ds:Signature elements carry none of their own), pass the wrapper root — a bare ds:Signature element cannot reproduce the ancestor-declared context that the signed digests were computed against.
In a multi-signature wrapper, Validate selects the first signature whose references target the passed element. If no reference matches, the last signature found is used — but only when the passed element has no Id (it cannot be targeted by a reference URI) or is itself that signature; use ValidateSig to select a specific signature by its Id.
func (*ValidationContext) ValidateSig ¶ added in v0.3.0
func (ctx *ValidationContext) ValidateSig(el *etree.Element, sigId string) error
ValidateSig validates the ds:Signature element whose Id attribute equals sigId, found within el. el must be the namespace-complete context of the signed document (e.g. the BDOC wrapper root) — see Validate.
type XMLSignatureBuilder ¶
type XMLSignatureBuilder struct {
IdAttribute string
Prefix string
// contains filtered or unexported fields
}
XMLSignatureBuilder constructs a complete XML Digital Signature (<ds:Signature>) ready for serialization and cryptographic signing.
Fluent API ¶
Configure the builder by chaining method calls, then invoke Sign to produce the signed XML. Every setter returns *XMLSignatureBuilder for chaining.
Typical Construction Flow ¶
- Create a builder: NewXMLSignatureBuilder(dm)
- (Optional) Customize settings: WithIdAttribute, WithPrefix, WithCanonicalizer, WithDigestMethod
- Attach the element to sign: WithParent(parentElem, idsToSign)
- (Optional) Embed extra data: WithObject(childElem, idsToSign)
- (Optional) Attach identity info: WithCerts(certDERs), WithKeyName(name)
- (Optional) Add detached refs: WithExternalReferences(urls), WithExternalRef(extRef)
- (Optional) Append UNSIGNED qualifying sig properties: WithUnsignedSignatureProperties(...) 7b. (Optional) Append SIGNED qualifying sig properties (QSCD/EIDAS): WithSignedSignatureProperties(...)
- Serialize + sign: BuildSignature()
Method Interaction Rules ¶
- WithParent must be called before Build. Its argument determines what element serves as the root of the signature graph.
- WithCerts and WithKeyName both initialize b.keyInfo lazily. Calling either one first ensures the KeyInfo subtree is created before the next call appends to it. Order between these two does not matter.
- Objects accumulate: repeated WithObject/WithObjects calls append; they do not replace prior entries.
- External references are processed in the order they are registered.
Default Configuration ¶
Unless overridden, the builder uses:
- ID attribute: "Id" (W3C-spec compliant; overrides legacy "ID")
- Prefix: "ds"
- Canonicalizer: Exclusive C14N 1.1
- Digest algorithm: SHA-256
Errors ¶
Building a signature never returns an error directly. Errors surface during the subsequent Sign() call as structured errors wrapping package-level sentinel values (e.g., ErrParentElementNotFound). Inspect with errors.Is().
A builder is single-use: BuildSignature mutates its state in place and may be called at most once — create a new builder for each signature.
func NewXMLSignatureBuilder ¶
func NewXMLSignatureBuilder(dm crypto.XMLDigestModule) *XMLSignatureBuilder
NewXMLSignatureBuilder initializes a builder with sensible defaults.
Parameters:
- dm: the digest module providing hash-function lookup and computation. Obtain via crypto.StandardDigestModule{}.
Defaults:
IdAttribute: "Id" (W3C XML-DSIG spec; camelCase) Prefix: "ds" (standard XML-DSIG namespace prefix) Canonicalizer: C14N 1.1 (exclusive, most efficient for modern parsers) DigestMethod: SHA-256 (secure default; upgrade to SHA-384/512 for HSM-grade)
The caller retains responsibility for supplying a private key and certificate chain to the Sign() method. This constructor only sets up the XML structure.
func (*XMLSignatureBuilder) BuildSignature ¶
func (b *XMLSignatureBuilder) BuildSignature(signerModule crypto.XMLSignatureSignerModule) (*etree.Element, error)
Returns the signature element. If parent was set, it will have the signature appended to it in memory (when WithRoot is also set, the parent must be the root itself).
The returned element is the signature node of the live tree (attached to the parent/root when configured); the caller owns serialization (XML declaration, indentation, base64 wrapping) — the library bakes no formatting into the document.
func (*XMLSignatureBuilder) CanonicalizeElement ¶ added in v0.3.0
func (b *XMLSignatureBuilder) CanonicalizeElement(el *etree.Element) ([]byte, error)
CanonicalizeElement canonicalizes an element of the current in-memory tree (as built by BuildSignature — e.g. a ds:SignatureValue element) at its in-tree location, using the builder's canonicalizer. This is the primitive for two-phase signature timestamping: after signing, canonicalize the target element, use the returned bytes as the TST imprint (hash them for the timestamp request), append the (unsigned) timestamp element to the tree, and re-serialize. Appending unsigned elements does not change the canonical form of the signed regions, so the signature remains valid after re-serialization.
el must be attached to a tree; a detached element is canonicalized with the default namespace context (its own declarations only).
func (*XMLSignatureBuilder) WithCanonicalizer ¶
func (b *XMLSignatureBuilder) WithCanonicalizer(canonicalizer canonicalizers.Canonicalizer) *XMLSignatureBuilder
WithCanonicalizer selects the canonicalization algorithm applied to SignedInfo and to each reference before hashing.
Available choices (from the canonicalizers package):
MakeC14N11Canonicalizer() - Exclusive C14N 1.1 (recommended, fastest) MakeC14N11CommentCanonicalizer() - C14N 1.1 retaining comments MakeExcC14NCanonicalizer() - Exclusive C14N 1.0 (legacy) MakeExcC14NCommentCanonicalizer() - C14N 1.0 retaining comments MakeCanonCanonicalizer() - Literal C14N 1.0 (broader compat, slower) MakeCanonCommentCanonicalizer() - C14N 1.0 retaining comments
The canonicalizer chosen here also applies to external references parsed as XML. For non-XML external resources, raw bytes are hashed directly.
func (*XMLSignatureBuilder) WithCerts ¶
func (b *XMLSignatureBuilder) WithCerts(certsDer [][]byte)
WithCerts populates the KeyInfo with X509 certificates.
Each DER-encoded certificate is base64-encoded and placed inside a <ds:X509Certificate> element within <ds:X509Data>.
If no KeyInfo exists yet, this method creates one. Subsequent calls to WithCerts append to the existing X509Data (do not replace it). Combine with WithKeyName for a complete KeyInfo.
func (*XMLSignatureBuilder) WithDigestMethod ¶
func (b *XMLSignatureBuilder) WithDigestMethod(digestMethod spec.XMLDigestAlgorithmID) *XMLSignatureBuilder
WithDigestMethod selects the hash algorithm for computing reference digests.
Accepted values (from spec package constants):
SHA256DigestAlgorithmId - SHA-256 (recommended minimum) SHA384DigestAlgorithmId - SHA-384 (stronger) SHA512DigestAlgorithmId - SHA-512 (strongest widely supported) SHA1DigestAlgorithmId - SHA-1 (deprecated; always rejected — GetDigestFunc fails with ErrUnsupportedDigestAlgorithm)
The digest algorithm must match what the crypto.SignerModule expects.
func (*XMLSignatureBuilder) WithEmbeddedElement ¶ added in v0.3.0
func (b *XMLSignatureBuilder) WithEmbeddedElement(e EmbeddedElement) *XMLSignatureBuilder
WithEmbeddedElement registers a caller-built element to embed and cryptographically bind into SignedInfo.
The builder deep-COPIES the element into the signature (the caller's tree is not mutated — the same copy semantics as WithObject and WithSignedSignatureProperties). Multiple embedded elements are placed and bound in registration order. The builder is single-use with respect to embedded elements: BuildSignature consumes them (even on a late failure), so a reused builder will not re-sign them. See EmbeddedElement for the placement rules (InObject) and the Id / namespace requirements on Element.
When RefTargetId is set, the emitted reference instead targets a DESCENDANT of the element carrying that Id and the digest is computed over that descendant at its final in-document position. This is the standard XAdES 1.3.2 QP → SP pattern: xades:QualifyingProperties (Target only, no Id) inside ds:Object, referenced via the nested xades:SignedProperties' Id.
func (*XMLSignatureBuilder) WithExternalRef ¶
func (b *XMLSignatureBuilder) WithExternalRef(r ExternalRef) *XMLSignatureBuilder
WithExternalRef registers a single detached reference backed by in-memory data — no network I/O occurs.
Parameters ¶
- r.URI: the reference URI (used in <Reference URI="...">). May be empty to refer to the root element, or a relative/local path for clarity.
- r.Data: the raw payload bytes. If parseable as XML, the builder's canonicalizer processes the data before hashing. Otherwise, raw bytes are hashed directly (identity canonicalization).
Difference from WithExternalReferences ¶
- WithExternalRef: eager, in-memory, zero I/O. Ideal for testing or when the payload is already available locally.
- WithExternalReferences: lazy, network-fetched. Ideal for remote resources.
Example ¶
receiptJSON := []byte(`{"amount": 42.00}`)
b.WithExternalRef(xmlsig.ExternalRef{
URI: "/receipt",
Data: receiptJSON,
})
func (*XMLSignatureBuilder) WithExternalReferences ¶
func (b *XMLSignatureBuilder) WithExternalReferences(uris []string) *XMLSignatureBuilder
WithExternalReferences registers detached-reference URIs whose payloads are fetched at sign-time.
How It Works ¶
For each URI:
- At sign time, the library performs an HTTP GET to retrieve the resource.
- If the response parses as well-formed XML, the canonicalizer (chosen via WithCanonicalizer) processes it before hashing.
- If the response is NOT valid XML (binary, JSON, etc.), the raw bytes are hashed directly.
Limitations ¶
- HTTP requests use a 10-second timeout with no configurability.
- Redirects are followed (etree's transport uses net/http defaults).
- Authentication headers, TLS client certs, and proxy configuration are not supported. For advanced scenarios, use WithExternalRef with manually fetched data.
Example ¶
b.WithExternalReferences([]string{
"https://example.com/invoice.pdf",
"https://example.com/receipt.json",
})
func (*XMLSignatureBuilder) WithIdAttribute ¶
func (b *XMLSignatureBuilder) WithIdAttribute(idAttribute string) *XMLSignatureBuilder
WithIdAttribute sets the name of the attribute used to identify XML elements for signing and referencing.
Per W3C XML-DSIG, the recommended attribute is "Id" (camelCase). Override only when interacting with legacy systems that use "ID" (uppercase) or a custom name.
Default: "Id"
func (*XMLSignatureBuilder) WithKeyInfo ¶
func (b *XMLSignatureBuilder) WithKeyInfo(keyInfo *etree.Element) *XMLSignatureBuilder
WithKeyInfo replaces the entire KeyInfo subtree with a caller-supplied element.
Use this when you need full control over KeyInfo content (e.g., embedding RSAKeyValue, RetrievalMethod, or proprietary extensions). If called after WithCerts or WithKeyName, those previously-added elements are discarded.
For simple cases, prefer WithCerts and/or WithKeyName which build a standard X509Data/KeyName structure automatically.
func (*XMLSignatureBuilder) WithKeyName ¶
func (b *XMLSignatureBuilder) WithKeyName(name string) *XMLSignatureBuilder
WithKeyName adds a <KeyName> element to the KeyInfo for identifying the key.
The key name is an opaque string chosen by the signer. Validators compare it against local policy (e.g., "match the key named 'invoice-server'").
If no KeyInfo exists yet, this method creates one. Combining with WithCerts produces a standard KeyInfo carrying both identity (KeyName) and material (X509Data).
func (*XMLSignatureBuilder) WithObject ¶
func (b *XMLSignatureBuilder) WithObject(element *etree.Element, IDsToSign []string) *XMLSignatureBuilder
WithObject embeds an XML element inside the signature as a <ds:Object> child and optionally references its descendants for signing.
Purpose ¶
An Object lets you carry additional data alongside the signature — for example, embedding the signed invoice body within an enveloping signature so the recipient receives everything in one blob.
How It Works ¶
- The supplied element is deep-copied into a newly-created <ds:Object>.
- If IDsToSign contains SignEntireElement, the Object itself gets an Id attribute and a corresponding <Reference> pointing to it.
- If IDsToSign lists concrete element IDs, each is turned into a <Reference URI="#<id>"> inside SignedInfo.
- The Object receives an auto-generated Id ("objN") for internal tracking.
Parameters ¶
- element: the XML element to embed. Must belong to a known namespace.
- IDsToSign: which descendants (or self) to include in SignedInfo.<References>. Use SignEntireElement to sign the whole Object, or a list of concrete element IDs for granular references.
Example ¶
// Embed an <Item> element and sign both the Item and its <Price> child
body := doc.SelectElements("//ns:Body", nsMap)[0]
item := body.FindElement("ns:Item")
item.CreateAttr("Id", "item-ref")
price := item.FindElement("ns:Price")
price.CreateAttr("Id", "price-val")
b.WithObject(item, []string{
xmlsig.SignEntireElement,
"price-val",
})
Notes ¶
- Calling WithObject multiple times appends multiple <ds:Object> elements.
- Objects are independent — references inside one Object do not reach into another Object or into the parent element.
- Deprecated: passing an empty string ("") in IDsToSign is equivalent to SignEntireElement but will trigger a lint warning in strict mode.
func (*XMLSignatureBuilder) WithObjects ¶
func (b *XMLSignatureBuilder) WithObjects(objects []ObjectEntry) *XMLSignatureBuilder
WithObjects appends multiple ObjectEntries in a single call. Equivalent to calling WithObject repeatedly, but avoids per-call overhead.
See WithObject for the semantics of ObjectEntry, IDsToSign, and SignEntireElement.
func (*XMLSignatureBuilder) WithParent ¶
func (b *XMLSignatureBuilder) WithParent(parent *etree.Element, IDsToSign []string) *XMLSignatureBuilder
WithParent declares the root element to sign and which of its descendants to reference.
Purpose ¶
The parent element is the anchor of the signature graph. For enveloped signatures, the Signature becomes a child of this element. For detached signatures, this element serves as the implicit reference target when IDsToSign is empty.
How IDsToSign Works ¶
- SignEntireElement: sign the entire parent element as an enveloped signature (adds enveloped-signature transform automatically).
- Concrete IDs like []string{"child1", "child2"}: create individual <Reference URI="#child1"> entries targeting descendant elements.
- Avoid empty string (""): deprecated, behaves like SignEntireElement.
Example ¶
// Sign the <Invoice> element and its <LineItem> children
invoice := doc.FindElement("Invoice")
invoice.CreateAttr("Id", "inv-root")
for i, item := range doc.FindElements("//LineItem") {
item.CreateAttr("Id", fmt.Sprintf("line-%d", i))
}
b.WithParent(invoice, []string{
xmlsig.SignEntireElement,
"line-0", "line-1", "line-2",
})
func (*XMLSignatureBuilder) WithPrefix ¶
func (b *XMLSignatureBuilder) WithPrefix(prefix string) *XMLSignatureBuilder
WithPrefix sets the namespace prefix used for XML-DSIG elements.
The conventional prefix is "ds" mapping to http://www.w3.org/2000/09/xmldsig#. Changing this alters the serialized output and may break interoperability with off-the-shelf validators.
Default: "ds"
func (*XMLSignatureBuilder) WithRoot ¶ added in v0.3.0
func (b *XMLSignatureBuilder) WithRoot(root *etree.Element) *XMLSignatureBuilder
WithRoot places the constructed ds:Signature under the caller-supplied wrapper root element (e.g. a BDOC asic:XAdESSignatures root). The root is a container, NOT a signing target — no reference is created for it. The signature is appended to the root at sign time, so reference digests (embedded elements, in-document contexts) reflect the final document context.
The ds:Signature element omits its own xmlns:<prefix> declaration when the prefix is already bound to the XML-DSig namespace in the in-scope namespace context at the root's position; otherwise the declaration is emitted.
Multiple detached signatures under one root: create one builder per signature, each WithRoot(sameRoot); signatures appear in the order their builders are run. WithRoot may be combined with WithParent only when the parent IS the root itself (an enveloped signature inside the wrapper root). When both are set and the parent differs from the root, BuildSignature fails fast — the enveloped digest targets the parent while the signature lives under the root, which would yield a document no validator can verify.
Validation: pass the wrapper root to ValidationContext.Validate (selects the first matching signature) or ValidationContext.ValidateSig(root, sigId) for a specific signature — the root provides the namespace context the signed digests were computed against.
func (*XMLSignatureBuilder) WithSignatureId ¶
func (b *XMLSignatureBuilder) WithSignatureId(signatureId string) *XMLSignatureBuilder
WithSignatureId sets an explicit Id attribute on the outermost <ds:Signature> element.
Leave blank (default) to let the library assign a generated UUID-like value.
Setting a custom ID enables other signatures or policies to reference this specific signature by URI fragment (e.g., #mysig).
func (*XMLSignatureBuilder) WithSignedPropertiesId ¶ added in v0.3.0
func (b *XMLSignatureBuilder) WithSignedPropertiesId(id string) *XMLSignatureBuilder
WithSignedPropertiesId sets a deterministic Id for the <ds:SignedProperties> wrapper created by WithSignedSignatureProperties (default: wall-clock "sp-<UnixNano>"). The wrapper Id and its SignedInfo reference URI are set from this value, so combined with WithSignatureId the legacy mechanism's output carries no wall-clock Ids.
func (*XMLSignatureBuilder) WithSignedSignatureProperties
deprecated
func (b *XMLSignatureBuilder) WithSignedSignatureProperties( props ...*etree.Element, ) *XMLSignatureBuilder
WithSignedSignatureProperties attaches qualifying-signature metadata that IS cryptographically bound to the signature via a <Reference> in <SignedInfo>.
This method:
- Wraps the supplied <ds:SignatureProperty> elements in a <ds:SignedProperties> wrapper element in the ds: (XML-DSig) namespace, placed directly under <ds:Signature>, with a wall-clock auto-generated Id ("sp-<UnixNano>").
- Sets the Target attribute on each <SignatureProperty> to point to the <ds:Signature> element (using its Id).
- Injects a <Reference URI="#<signedPropsId">> into <SignedInfo> that DECLARES the builder's canonicalization transform.
The resulting structure:
<ds:Signature Id="sig-1">
...
<ds:SignedProperties Id="sp-1">
<ds:SignatureProperty Target="#sig-1">
<etsi:SigningTime>2025-01-15T10:30:00Z</etsi:SigningTime>
</ds:SignatureProperty>
</ds:SignedProperties>
</ds:Signature>
This emitted wrapper is NOT an XAdES element — XAdES SignedProperties belongs to the ETSI xades: namespace and is built by the caller — and it is not part of the base XML-DSig schema. Do not treat this method as an XAdES/BDOC conformance mechanism.
Typical Usage ¶
// Create a SigningTime claim cpDoc := etree.NewDocument() cpDoc.ReadFromString(`<ds:SignatureProperty xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:etsi="http://uri.etsi.org/01903/v1.3.2#"> <etsi:SigningTime>2025-01-15T10:30:00Z</etsi:SigningTime>
</ds:SignatureProperty>`)
b.WithSignedSignatureProperties(cpDoc.Root())
Warning ¶
Each supplied element must be a <ds:SignatureProperty>. The builder wraps them in <ds:SignedProperties>; do NOT pass a <ds:SignedProperties> element itself. Namespace prefixes on the <SignatureProperty> children are preserved as-is.
Deprecated: use WithEmbeddedElement with a caller-built element in the appropriate profile namespace (e.g. xades:SignedProperties) for new code. Together with WithSignatureId and WithSignedPropertiesId the output is fully deterministic (no wall-clock Ids).
func (*XMLSignatureBuilder) WithUnsignedSignatureProperties ¶
func (b *XMLSignatureBuilder) WithUnsignedSignatureProperties( props ...*etree.Element, ) *XMLSignatureBuilder
WithUnsignedSignatureProperties attaches unqualified metadata elements directly beneath <ds:Signature>.
Per XML-DSIG §5.5, <SignatureProperties> conveys supplemental information such as timestamps, policy identifiers, or countersignatures. This method inserts whatever elements you pass directly — the caller is responsible for wrapping them in <ds:SignatureProperties> if desired.
These elements are NOT included in <SignedInfo>/<References> and therefore are NOT cryptographically protected by the signature.
Typical Usage ¶
// Unqualified timestamp — visible but not signed
tp := etree.NewElement("ds:SignatureProperties")
tp.Space = "ds"
tp.CreateAttr("Id", "ts-unqual")
tp.AddChild(etree.NewElement("ds:SignatureProperty").SetText("2025-01-15T10:30:00Z"))
b.WithUnsignedSignatureProperties(tp)
Warning ¶
The caller is responsible for ensuring correct namespace prefixes and element names. Incorrectly formed elements will produce signatures that fail schema validation.