The Nokogiri API (Recommended)
HankoXml can extend Nokogiri directly, making signing and verifying incredibly ergonomic. The
extension is opt-in: require "hanko_xml" alone leaves Nokogiri’s classes untouched, and
require "hanko_xml/nokogiri_ext" adds these methods to Nokogiri::XML::Document and Node:
| Method | Same as |
|---|---|
doc.sign(**opts) |
HankoXml.sign(doc, **opts), a new SignedDocument; doc is untouched |
doc.sign!(**opts) |
HankoXml.sign!(doc, **opts), signs in place and returns doc |
doc.signature_valid?(**opts) |
HankoXml.valid?(doc, **opts), true / false |
doc.signatures |
the <ds:Signature> elements, a NodeSet |
doc.signature_present? |
HankoXml.signature_present?(doc), structural, no crypto |
require "nokogiri"
require "hanko_xml"
require "hanko_xml/nokogiri_ext"
doc = Nokogiri::XML(File.read("document.xml"))
private_key = File.read("private.pem")
certificate = File.read("public.crt")
# Sign the document in place
doc.sign!(key: private_key, certificate: certificate)
# Safely serialize without destroying the signature bytes
File.write("signed.xml", doc.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML))
# Verify it later
signed_doc = Nokogiri::XML(File.read("signed.xml"))
if signed_doc.signature_valid?(certificate: certificate)
puts "Signature is valid!"
end
Why save_with: AS_XML?
Re-serializing a signed document with the wrong options can silently change whitespace or attribute
quoting, invalidating the byte-exact canonicalization the signature relies on. Always serialize a
signed document with Nokogiri::XML::Node::SaveOptions::AS_XML (no formatting, no entity
substitution) so the bytes you write are the bytes that were actually signed.
Every keyword from Signer/Verifier is accepted
The extension’s methods forward every keyword accepted by HankoXml::Signer.new /
HankoXml::Verifier.new — see the API Reference for the full list, including
policy:, placement:, signature_method:, and trust options like store:.
doc.sign!(key: key, certificate: certificate, signature_method: :rsa_sha256, policy: HankoXml::Policy.strict)
doc.signature_valid?(store: my_trusted_ca_store, verification_time: Time.parse("2023-01-01"))
Next: the Module API for non-Nokogiri-mutating equivalents, or
XSW Defense for the security model behind signed?.