Passo 1 - Certificado e Chave privada

$ private_key = OpenSSL::PKey::RSA.new(File.read("caminho_para_a_chave_privada"), "senha")
$ certificate = OpenSSL::X509::Certificate.new(File.read("caminho_para_o_certificado"))

Se você usa o formato PFX, P12, arquivos em binário, você extrai a chave primária e o certificado assim

$ openssl pkcs12 -in certificado.pfx -nocerts -out chave_privada.pem
$ openssl pkcs12 -in certificado.pfx -clcerts -nokeys -out certificado_publico.pem

Passo 2 - Cálculo do DigestValue

Segue um exemplo
<infNFe Id="NFe00000000000000000000000000000000000000000000" versao="3.10">
  <ide>
  </ide>
  <emit>
  </emit>
  <dest>
  </dest>
  <det nItem="1">
    <prod>
    </prod>
    <imposto>
    </imposto>
  </det>
  <total>
    <ICMSTot>
    </ICMSTot>
  </total>
  <transp>
  </transp>
</infNFe>

O primeiro segredo que não lhe contaram é que você deve acrescentar o namespace xmlns com o valor http://www.portalfiscal.inf.br/nfe na tag infNFe

Então o xml ficará assim

<infNFe xmlns="http://www.portalfiscal.inf.br/nfe" Id="NFe00000000000000000000000000000000000000000000" versao="3.10">
  ...
</infNFe>

Agora você canoniza esse xml com o Nokogiri

inf_nfe = %{...}
infnfe_canonized = Nokogiri::XML(inf_nfe.gsub(/>\s+</,"><")).canonicalize(Nokogiri::XML::XML_C14N_1_1)

Calculando o DigestValue com o SHA1.

digest_value = Base64.encode64(OpenSSL::Digest::SHA1.digest(infnfe_canonized)).strip

Passo 3 - Assinatura Digital

Segue o exemplo do xml completo da assinatura

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
  <SignedInfo>
    <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <Reference URI="#NFe00000000000000000000000000000000000000000000">
      <Transforms>
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      </Transforms>
      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
      <DigestValue>?</DigestValue>
    </Reference>
  </SignedInfo>
  <SignatureValue>?</SignatureValue>
  <KeyInfo>
    <X509Data>
      <X509Certificate>?</X509Certificate>
    </X509Data>
  </KeyInfo>
</Signature>

Você irá mudar:

  • O namespace URI na tag Reference
  • O DigestValue
  • O SignatureValue

Aqui, o segundo segredo. Você vai assinar a tag SignedInfo, mas antes de assinar a tag vc vai acrescentar o namespace xmlns e atribuir o valor http://www.w3.org/2000/09/xmldsig#, deixando-a assim

<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
  <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
  <Reference URI="#NFe?">
    <Transforms>
      <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
      <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    </Transforms>
    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <DigestValue>?</DigestValue>
  </Reference>
</SignedInfo>

Feito isso, remova o espaço entre as tags, canonize a string e assine com a chave privada, o resultado da assinatura vc colocará na tag SignatureValue

signed_info = %{...}
signed_info_canonized = Nokogiri::XML(signed_info.gsub(/>\s+</,"><")).canonicalize(Nokogiri::XML::XML_C14N_1_1)
signature_value = Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, signed_info_canonized))

Passo 4 - O X509Certificate

certificate.to_s.gsub(/\-+[A-Z]+ CERTIFICATE\-+/, "").strip()

Detalhe importante: os namespace adicionados nas tags infNFe e SignedInfo são usados apenas para o cálculo do DigestValue e a assinatura digital, feito o procedimento você deve remover esses namespaces

Ainda acha que está complicado? Dá uma olhadinha nesta na gem signature_dfe


🗓 2016-08-21
1785 👀