Skip to content

Instantly share code, notes, and snippets.

@guilpejon
Created January 25, 2018 22:27
Show Gist options
  • Save guilpejon/bab4ac4cdbb34d9d595288ff4fa6fb38 to your computer and use it in GitHub Desktop.
Save guilpejon/bab4ac4cdbb34d9d595288ff4fa6fb38 to your computer and use it in GitHub Desktop.
An example on how to validate XMLNS digital signature
require "nokogiri"
require "openssl"
require "base64"
C14N = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
SHA_MAP = {
1 => OpenSSL::Digest::SHA1,
256 => OpenSSL::Digest::SHA256,
384 => OpenSSL::Digest::SHA384,
512 => OpenSSL::Digest::SHA512
}
# Set up the certificate
certificate = OpenSSL::X509::Certificate.new(Base64.decode64(file.xpath("//X509Certificate").to_s[17..-19]))
# Read the document and the signature
document = Nokogiri::XML(File.read("nota.xml"))
signature = document.at('//xmlns:Signature', xmlns: 'http://www.w3.org/2000/09/xmldsig#')
# Get signature ref and digest
ref = original.at('//xmlns:Signature//xmlns:SignedInfo//xmlns:Reference', xmlns: 'http://www.w3.org/2000/09/xmldsig#')
digest_value = ref.at("./xmlns:DigestValue", xmlns: 'http://www.w3.org/2000/09/xmldsig#').text
decoded_digest_value = Base64.decode64(digest_value)
ref_canoned = document.at("//xmlns:infNFe", xmlns: "http://www.portalfiscal.inf.br/nfe").canonicalize(C14N)
# Discover which method was use to sign the digest
digest_method = OpenSSL::Digest::SHA1
if ref.at("./xmlns:DigestMethod/@Algorithm", xmlns: 'http://www.w3.org/2000/09/xmldsig#').text =~ /sha(\d+)$/
digest_method = SHA_MAP[$1.to_i]
end
# Verify the digest
digest = digest_method.digest(ref_canoned)
digest == decoded_digest_value
# Read canonical SignedInfo
signed_info = original.at('//xmlns:Signature//xmlns:SignedInfo', xmlns: 'http://www.w3.org/2000/09/xmldsig#')
canoned = signed_info.canonicalize(C14N)
# Figure out which method has been used to the sign the signature
signature_method = OpenSSL::Digest::SHA1
if signature.at("./xmlns:SignedInfo/xmlns:SignatureMethod/@Algorithm", xmlns: 'http://www.w3.org/2000/09/xmldsig#').text =~ /sha(\d+)$/
signature_method = SHA_MAP[$1.to_i]
end
# Read the signature
signature_value = signature.at("./xmlns:SignatureValue", xmlns: 'http://www.w3.org/2000/09/xmldsig#').text
decoded_signature_value = Base64.decode64(signature_value)
# Verify that the signature is correct
certificate.public_key.verify(signature_method.new, decoded_signature_value, canoned)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment