The Module API
If you prefer not to load the Nokogiri extension (hanko_xml/nokogiri_ext), you can use the top-level HankoXml module
directly. It is the 90% case: constructor + single call, nothing more.
require "hanko_xml"
# Sign. Returns a NEW HankoXml::SignedDocument; `doc` is untouched.
signed = HankoXml.sign(doc, key: key, certificate: cert)
signed.to_xml # signature-safe serialization; #to_s and #write_to too
# Sign in place. Returns the same object you passed, now containing <Signature>.
HankoXml.sign!(doc, key: key, certificate: cert)
# Verify — predicate. Safe to use directly in a conditional.
HankoXml.valid?(xml, certificate: cert) # => true / false
# Verify — raising. Returns a Verification; raises on anything but success.
HankoXml.verify!(xml, certificate: cert) # => Verification, or raises
# Is there a signature at all? (structural, no crypto)
HankoXml.signature_present?(xml) # => true / false
valid?, verify! and signature_present? work without Nokogiri, on a String, a path or an
IO. Without Nokogiri, sign signs a document that already holds its <Signature> template, and
returns a bytes-backed SignedDocument. Building a template, and sign!, need Nokogiri
(require "nokogiri"); without it they raise HankoXml::NokogiriRequired.
Verification
require "hanko_xml"
require "pathname"
xml = File.read("signed.xml")
# Raises HankoXml::VerificationError if the signature is invalid or tampered with
result = HankoXml.verify!(xml, certificate: Pathname("public.crt"))
puts result.valid? # => true
Signing (immutable)
# Returns a safe `SignedDocument` wrapper without mutating the original
# A String is PEM content, not a filename: pass files as Pathname (or File.read them).
signed_doc = HankoXml.sign(xml, key: Pathname("private.pem"), certificate: Pathname("public.crt"))
puts signed_doc.to_s
There is deliberately no HankoXml.verify
Only sign / sign! and verify! / valid? / signature_present? are exposed at the module level. There is
no HankoXml.verify returning a rich object: a Verification instance is always truthy, so
if HankoXml.verify(doc, ...) would be a security bug that reads like correct code. The rich
Verification object (see Reference: Verification) is reachable only
through verify! (which raises on failure) or HankoXml::Verifier#verify, where the object’s role
is explicit. This asymmetry is intentional — see XSW Defense for why it matters.
For fine-grained control over algorithms, placement, and trust sources, construct a
Signer or Verifier directly.