Class: Facturae::Xades::Signer

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/facturae/xades/signer.rb

Overview

Handles the signing of XML documents using XAdES (XML Advanced Electronic Signatures). This implementation follows the XAdES-BES profile and Facturae 3.2.2 specifications.

The signing process follows these steps: 1. Build the signature structure 2. Canonicalize the SignedInfo 3. Calculate the signature value 4. Add all required XAdES elements 5. Validate the complete structure

Examples:

xml_doc = Nokogiri::XML(xml_string)
private_key = OpenSSL::PKey::RSA.new(key_file)
certificate = OpenSSL::X509::Certificate.new(cert_file)

signer = Signer.new(xml_doc, private_key, certificate)
signer.sign

Raises:

Constant Summary collapse

XADES_NAMESPACE =
"http://uri.etsi.org/01903/v1.3.2#"
XMLDSIG_NAMESPACE =
"http://www.w3.org/2000/09/xmldsig#"
C14N_METHOD_ALGORITHM =
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
SIGNATURE_METHOD_ALGORITHM =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
NAMESPACES =
{
  "ds" => XMLDSIG_NAMESPACE,
  "xades" => XADES_NAMESPACE
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#base64_encode, #base64_encode_raw, #calculate_sha512_digest, #create_xml_element, #create_xml_node_with_algorithm, #rand_id

Constructor Details

#initialize(xml_doc, private_key, certificate, builders = {}) ⇒ Signer

Initialize a new XAdES signer

Parameters:

  • xml_doc (Nokogiri::XML::Document)

    The XML document to sign

  • private_key (OpenSSL::PKey::RSA)

    The private key for signing

  • certificate (OpenSSL::X509::Certificate)

    The X509 certificate

  • builders (Hash) (defaults to: {})

    Optional hash with builder classes for dependency injection



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/facturae/xades/signer.rb', line 64

def initialize(xml_doc, private_key, certificate, builders = {})
  @xml_doc = xml_doc
  @private_key = private_key
  @certificate = certificate
  @builders = builders

  @certificate_id = "Certificate#{rand_id}"
  @reference_id = "Reference-ID-#{rand_id}"
  @signature_id = "Signature#{rand_id}"
  @signature_object_id = "#{signature_id}-Object#{rand_id}"
  @signature_value_id = "SignatureValue#{rand_id}"
  @signed_info_id = "Signature-SignedInfo#{rand_id}"
  @signed_properties_id = "SignedPropertiesID#{rand_id}"

  # Register namespaces in the document
  register_namespaces
end

Instance Attribute Details

#certificate_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def certificate_id
  @certificate_id
end

#reference_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def reference_id
  @reference_id
end

#signature_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def signature_id
  @signature_id
end

#signature_object_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def signature_object_id
  @signature_object_id
end

#signature_value_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def signature_value_id
  @signature_value_id
end

#signed_info_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def signed_info_id
  @signed_info_id
end

#signed_properties_idNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def signed_properties_id
  @signed_properties_id
end

#xml_docNokogiri::XML::Document, String (readonly)

Returns:

  • (Nokogiri::XML::Document)

    The XML document to sign

  • (String)

    The ID of the signature node

  • (String)

    The ID of the signed properties

  • (String)

    The ID of the signature object

  • (String)

    The ID of the reference

  • (String)

    The ID of the certificate

  • (String)

    The ID of the signature value

  • (String)

    The ID of the signed info



55
56
57
# File 'lib/facturae/xades/signer.rb', line 55

def xml_doc
  @xml_doc
end

Instance Method Details

#signNokogiri::XML::Node

Sign the XML document using XAdES

Returns:

  • (Nokogiri::XML::Node)

    The signature node

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/facturae/xades/signer.rb', line 86

def sign
  signature_node = build_signature_node
  @xml_doc.root.add_child(signature_node)

  key_info_node, object_info_node = build_and_attach_info_nodes(signature_node)
  signed_info_node = build_and_attach_signed_info(signature_node)

  # Canonicalize SignedInfo in-place (as a subtree, not extracted)
  canonicalized_signed_info = signed_info_node.canonicalize(Nokogiri::XML::XML_C14N_1_0)
  signature_value_node = build_signature_value_node(calculate_signature(canonicalized_signed_info))

  reorder_signature_children(signature_node, signed_info_node, signature_value_node,
                             key_info_node, object_info_node)

  validate_xades_structure(signature_node)
  signature_node
end