Class: FE::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/facturacr/signer/signer.rb

Constant Summary collapse

C14N =
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
DSIG =
"http://www.w3.org/2000/09/xmldsig#"
NOKOGIRI_OPTIONS =
Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET | Nokogiri::XML::ParseOptions::NOENT
RSA_SHA1 =
"http://www.w3.org/2000/09/xmldsig#rsa-sha1"
RSA_SHA256 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
RSA_SHA384 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
RSA_SHA512 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
SHA1 =
"http://www.w3.org/2000/09/xmldsig#sha1"
SHA256 =
"http://www.w3.org/2001/04/xmlenc#sha256"
SHA384 =
"http://www.w3.org/2001/04/xmldsig-more#sha384"
SHA512 =
"http://www.w3.org/2001/04/xmlenc#sha512"
ENVELOPED_SIG =
"http://www.w3.org/2000/09/xmldsig#enveloped-signature"
INC_PREFIX_LIST =
"#default samlp saml ds xs xsi md"
NAMESPACES =
"#default ds xs xsi xades xsd"
XADES =
"http://uri.etsi.org/01903/v1.3.2#"
XADES141 =
"http://uri.etsi.org/01903/v1.4.1#"
SIGNATURE_POLICY_42 =
"https://tribunet.hacienda.go.cr/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf"
XMLNS_MAP_42 =
{
  "FacturaElectronica" => "https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/facturaElectronica",
  "NotaCreditoElectronica" => "https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaCreditoElectronica",
  "TiqueteElectronico" => "https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/tiqueteElectronico",
  "NotaDebitoElectronica" => "https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/notaDebitoElectronica",
  "MensajeReceptor" => "https://tribunet.hacienda.go.cr/docs/esquemas/2017/v4.2/mensajeReceptor"
}
XMLNS_MAP_43 =
{
  "FacturaElectronica" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/facturaElectronica",
  "NotaCreditoElectronica" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/notaCreditoElectronica",
  "TiqueteElectronico" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/tiqueteElectronico",
  "NotaDebitoElectronica" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/notaDebitoElectronica",
  "MensajeReceptor" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/mensajeReceptor",
  "FacturaElectronicaCompra" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/facturaElectronicaCompra",
  "FacturaElectronicaExportacion" => "https://cdn.comprobanteselectronicos.go.cr/xml-schemas/v4.3/facturaElectronicaExportacion"
}

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ Signer

Returns a new instance of Signer.

Raises:

  • (ArgumentError)
[View source]

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/facturacr/signer/signer.rb', line 45

def initialize(args = {})
  document_provider = args[:xml_provider]
  key_provider = args[:key_provider]
  pin = args[:pin]
  raise ArgumentError , "Los argumentos no son válidos" if document_provider.nil? || key_provider.nil? || pin.nil?
  @doc = Nokogiri::XML(document_provider.contents) do |config|
    config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NOENT | Nokogiri::XML::ParseOptions::NOENT
  end
  @p12 = OpenSSL::PKCS12.new(key_provider.contents,args[:pin])
  @x509 = @p12.certificate
  @output_path = args[:output_path]
  @document_tag = @doc.elements.first.name
  @version = @doc.elements.first.namespace.href.scan(/v4\..{1}/).first[1..-1]
  @xmlns_map = XMLNS_MAP_42 if @version.eql?("4.2")
  @xmlns_map = XMLNS_MAP_43 if @version.eql?("4.3")
end

Instance Method Details

#signObject

[View source]

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/facturacr/signer/signer.rb', line 62

def sign
  #Build parts for Digest Calculation
  key_info = build_key_info_element
  signed_properties = build_signed_properties_element
  signed_info_element = build_signed_info_element(key_info,signed_properties)
  
  # Compute Signature
  signed_info_canon = canonicalize_document(signed_info_element)
  signature_value = compute_signature(@p12.key,algorithm(RSA_SHA256).new,signed_info_canon)
              
  ds = Nokogiri::XML::Node.new("ds:Signature", @doc)
  ds["xmlns:ds"] = DSIG
  #ds["Id"] = SIGNATURE_ID#"xmldsig-#{uuid}"
  ds["Id"] = "xmldsig-#{uuid}"
  #ds.add_child(Nokogiri::XML(signed_info_without_ns).root)
  ds.add_child(signed_info_element.root)
  
  sv = Nokogiri::XML::Node.new("ds:SignatureValue", @doc)
  #sv["Id"] = SIGNATURE_VALUE#"xmldsig-#{uuid}-sigvalue"
  sv["Id"] = "xmldsig-#{uuid}-sigvalue"
  sv.content = signature_value
  ds.add_child(sv)
  
  ds.add_child(key_info.root)
  
  
  dsobj = Nokogiri::XML::Node.new("ds:Object",@doc)
  dsobj["Id"] = "xades-obj-#{uuid}"#XADES_OBJECT_ID
  qp = Nokogiri::XML::Node.new("xades:QualifyingProperties",@doc)
  qp["xmlns:xades"] = XADES
  #qp["Target"] = "##{SIGNATURE_ID}"#"#xmldsig-#{uuid}"
  qp["Target"] = "#xmldsig-#{uuid}"
  qp["Id"] = "QualifyingProperties-#{uuid}"
  qp.add_child(signed_properties.root)
  
  dsobj.add_child(qp)
  ds.add_child(dsobj)
  @doc.root.add_child(ds)
  
  File.open(@output_path,"w"){|f| f.write(@doc.to_xml(:save_with=>Nokogiri::XML::Node::SaveOptions::AS_XML).gsub(/\r|\n/,""))} if @output_path
  
  @doc.to_xml(:save_with=>Nokogiri::XML::Node::SaveOptions::AS_XML).gsub(/\r|\n/,"")
end