Ir para conteúdo
  • Cadastre-se

dev botao

  • Este tópico foi criado há 3773 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

  • Membros Pro
Postado

Utilizo ao ACBr para emissão de NFe e NFCe, e em ambos os casos tudo tem funcionado perfeitamente com a CAPICOM.

 

Também criei uma função com base na função NFeUtil.Assinar para assinar outro modelo de documento XML chamado de DAI (Declaração Amazonense de Importação), enquanto eu estava usando a versão anterior do ACBr eu conseguia assinar e validar estes arquivos de DAI no site da sefaz. Porém a poucos dias atualizei a versão do ACBR, devido a atualização para o padrão 3.10 do NFCe e e estes documentos de DAI passaram a ser rejeitos com a seguinte mensagem de retorno: Assinatura Inválida (Digest do Documento não Confere).  

 

O Interessante é quando utilize outro aplicativo para assinar o documento como XML Signer o digest value gerado é diferente do gerado por esta função que uso (detalhe estou usando o mesmo certificado digital para ambas aplicações).

 

Alguém pode me dar uma dica do que está acontecendo? Abaixo a função que uso e em anexo o arquivo assinado:  Desde já agradeço pelo suporte técnico.

 

function AssinarDIe(XML : AnsiString; Certificado : ICertificate2; out XMLAssinado : AnsiString): Boolean;
var
 I, J, PosIni, PosFim : Integer;
 URI           : String ;
 Tipo : Integer;
 
 xmlHeaderAntes, xmlHeaderDepois : AnsiString ;
 xmldoc  : IXMLDOMDocument3;
 xmldsig : IXMLDigitalSignature;
 dsigKey   : IXMLDSigKey;
 signedKey : IXMLDSigKey;
begin
   Assinatura:=TStringList.Create;
   Assinatura.Clear;
 
   if (Pos('<Signature',XML) <= 0) and (TipoAssinatura='Matriz') then //Assinar MatrizDIe
   begin
      I := pos('<InfMatrizDIe',XML) ;
      Tipo := 1;
 
      URI := 'MatrizDIe00000041483057' ;
 
      if Tipo = 1 then
         XML := copy(XML,1,pos('</MatrizDIe>',XML)-1);
 
      XML := XML + '<Reference URI="#'+URI+'">';
      XML := XML + '<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" />';
      XML := XML + '<DigestValue></DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo></KeyInfo></Signature>';
 
      if Tipo = 1 then
         XML := XML + '</MatrizDIe>'+'</enviMatrizDIe>';
     end
     Else begin                   //Assinar DIe
      I := pos('<InfDIe',XML) ;
      Tipo := 2;
 
      URI := URIDIe ;
     end;
 
   // Lendo Header antes de assinar //
   xmlHeaderAntes := '' ;
   I := pos('?>',XML) ;
   if I > 0 then
      xmlHeaderAntes := copy(XML,1,I+1) ;
 
   xmldoc := CoDOMDocument50.Create;
 
   xmldoc.async              := False;
   xmldoc.validateOnParse    := False;
   xmldoc.preserveWhiteSpace := True;
 
   xmldsig := CoMXDigitalSignature50.Create;
 
   if (not xmldoc.loadXML(XML) ) then begin
      Log:=TStringList.Create;
      Log.Text:=xml;
      Log.SaveToFile('C:\LogDAI.XML');
      raise EACBrNFeException.Create('Não foi possível carregar o arquivo (Foi Gerado um Log em C:\LogDAI.txt).');
   end;
 
   xmldoc.setProperty('SelectionNamespaces', DSIGNS);
 
   xmldsig.signature := xmldoc.selectSingleNode('.//ds:Signature');
 
   if (xmldsig.signature = nil) then
      raise EACBrNFeException.Create('É preciso carregar o template antes de assinar.');
 
   if NumCertCarregado <> Certificado.SerialNumber then
      CertStoreMem := nil;
 
   if  CertStoreMem = nil then
    begin
      CertStore := CoStore.Create;
      CertStore.Open(CAPICOM_CURRENT_USER_STORE, 'My', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
 
      CertStoreMem := CoStore.Create;
      CertStoreMem.Open(CAPICOM_MEMORY_STORE, 'Memoria', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
 
      Certs := CertStore.Certificates as ICertificates2;
      for i:= 1 to Certs.Count do
      begin
        Cert := IInterface(Certs.Item) as ICertificate2;
        if Cert.SerialNumber = Certificado.SerialNumber then
         begin
           CertStoreMem.Add(Cert);
           NumCertCarregado := Certificado.SerialNumber;
         end;
      end;
   end;
 
   OleCheck(IDispatch(Certificado.PrivateKey).QueryInterface(IPrivateKey,PrivateKey));
   xmldsig.store := CertStoreMem;
 
   dsigKey := xmldsig.createKeyFromCSP(PrivateKey.ProviderType, PrivateKey.ProviderName, PrivateKey.ContainerName, 0);
 
   if (dsigKey = nil) then
      raise EACBrNFeException.Create('Erro ao criar a chave do CSP.');
 
   signedKey := xmldsig.sign(dsigKey, $00000002);
 
   if (signedKey <> nil) then
    begin
      XMLAssinado := xmldoc.xml;
 
      PosIni := Pos('<SignatureValue>',XMLAssinado) + length('<SignatureValue>');
      PosFim := Pos('</SignatureValue>',XMLAssinado)-1;
 
      PosIni := Pos('<SignatureValue>',XMLAssinado)+length('<SignatureValue>');
 
      XMLAssinado := copy(XMLAssinado,1,PosIni-1) +
                     StringReplace( copy(XMLAssinado,PosIni,length(XMLAssinado)), ' ', '', [rfReplaceAll] ) ;
 
      PosIni := Pos('<X509Certificate>',XMLAssinado)-1;
      PosFim := DFeUtil.PosLast('<X509Certificate>',XMLAssinado);
 
      XMLAssinado := copy(XMLAssinado,1,PosIni)+copy(XMLAssinado,PosFim,length(XMLAssinado));
 
    end
   else
      raise EACBrNFeException.Create('Assinatura Falhou.');
 
   if xmlHeaderAntes <> '' then
   begin
      I := pos('?>',XMLAssinado) ;
      if I > 0 then
       begin
         xmlHeaderDepois := copy(XMLAssinado,1,I+1) ;
         if xmlHeaderAntes <> xmlHeaderDepois then
            XMLAssinado := StuffString(XMLAssinado,1,length(xmlHeaderDepois),xmlHeaderAntes) ;
       end
      else
         XMLAssinado := xmlHeaderAntes + XMLAssinado ;
   end ;
 
   dsigKey   := nil;
   signedKey := nil;
   xmldoc    := nil;
   xmldsig   := nil;
 
   Assinatura.Clear;
   Assinatura.Text:=XMLAssinado;
 
   Result := True;
end;
 

1413914987.xml

  • Curtir 1
  • Moderadores
Postado

1-Teste com a versão anterior e veja se o mesmo xml é assinado corretamente.

2-Teste com um XML menor para verificar se com todos acontecem o mesmo problema.

3-Remova quebras de linha.

4-Remova caracteres especiais.

5-Compare um arquivo assinado na versão anterior com a atual.

djsystem-logo.png
 youtube.png facebook.png instagram.png linkedin.png
André Ferreira de Moraes | Analista de Sistemas
www.djsystem.com.br | www.djpdv.com.br
www.tefhouse.com.br | www.xpos.com.br
  • Membros Pro
Postado

André, Valeu pelas dicas. Executei todos os procedimentos cima citados por você. Acredito que não estou conseguindo assinar corretamente o documento XML, pois após gerar o XML assinado eu invoco o método NFeUtil.ValidaAssinatura e ele me retorna a seguinte mensagem: "Erro ao verificar assinatura do arquivo: The digest value computed does not match with the provided one for Reference uri '#Die1401507761'.

 

É como se o digest fosse calculado para um arquivo e depois este arquivo é alterado. Porém para mim isto é muito estranho pois esta rotina é praticamente um clone da Rotina NFeUtil.AssinarMSXML, como você pode ver anteriormente. Em anexo estou te enviando um arquivo menor que estou usando para teste com versão assinada e outra não assinada, para ver se juntos encontramos o que pode estar acontecendo.

 

Acredito que seja algum detalhe que eu esteja cometendo de errado nesta rotina de assinar, mas como você tem mais experiência poderá me auxiliar melhor.

 

Desde já agradeço pela cooperação.

DIe1401507761notsingned.xml

DIe1401507761.xml

  • Moderadores
Postado

Usei a seguinte função e assinou normalmente(não comparei com a sua pra ver as diferenças):

 

function AssinarMSXML(XML : AnsiString; Certificado : ICertificate2; out XMLAssinado : AnsiString): Boolean;
var
 I, J, PosIni, PosFim : Integer;
 URI           : String ;
 Tipo : Integer;

 xmlHeaderAntes, xmlHeaderDepois : AnsiString ;
 xmldoc  : IXMLDOMDocument3;
 xmldsig : IXMLDigitalSignature;
 dsigKey   : IXMLDSigKey;
 signedKey : IXMLDSigKey;
begin
   if Pos('<Signature',XML) <= 0 then
   begin
      I := DFeUtil.PosEx('Id=',XML,6) ;
      if I = 0 then
         raise EACBrNFeException.Create('Não encontrei inicio do URI: Id=') ;
      I := DFeUtil.PosEx('"',XML,I+2) ;
      if I = 0 then
         raise EACBrNFeException.Create('Não encontrei inicio do URI: aspas inicial') ;
      J := DFeUtil.PosEx('"',XML,I+1) ;
      if J = 0 then
         raise EACBrNFeException.Create('Não encontrei inicio do URI: aspas final') ;

      URI := copy(XML,I+1,J-I-1) ;

      XML := copy(XML,1,pos('</DIe>',XML)-1);

      XML := XML + '<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" />';
      XML := XML + '<Reference URI="#'+URI+'">';
      XML := XML + '<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" />';
      XML := XML + '<DigestValue></DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo></KeyInfo></Signature>';

      XML := XML + '</DIe>';
   end;

   // Lendo Header antes de assinar //
   xmlHeaderAntes := '' ;
   I := pos('?>',XML) ;
   if I > 0 then
      xmlHeaderAntes := copy(XML,1,I+1) ;

   xmldoc := CoDOMDocument50.Create;

   xmldoc.async              := False;
   xmldoc.validateOnParse    := False;
   xmldoc.preserveWhiteSpace := True;

   xmldsig := CoMXDigitalSignature50.Create;

   if (not xmldoc.loadXML(XML) ) then
      raise EACBrNFeException.Create('Não foi possível carregar o arquivo: '+XML);

   xmldoc.setProperty('SelectionNamespaces', DSIGNS);

   xmldsig.signature := xmldoc.selectSingleNode('.//ds:Signature');

   if (xmldsig.signature = nil) then
      raise EACBrNFeException.Create('É preciso carregar o template antes de assinar.');

   if NumCertCarregado <> Certificado.SerialNumber then
      CertStoreMem := nil;

   if  CertStoreMem = nil then
    begin
      CertStore := CoStore.Create;
      CertStore.Open(CAPICOM_CURRENT_USER_STORE, 'My', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);

      CertStoreMem := CoStore.Create;
      CertStoreMem.Open(CAPICOM_MEMORY_STORE, 'Memoria', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);

      Certs := CertStore.Certificates as ICertificates2;
      for i:= 1 to Certs.Count do
      begin
        Cert := IInterface(Certs.Item[i]) as ICertificate2;
        if Cert.SerialNumber = Certificado.SerialNumber then
         begin
           CertStoreMem.Add(Cert);
           NumCertCarregado := Certificado.SerialNumber;
         end;
      end;
   end;

   OleCheck(IDispatch(Certificado.PrivateKey).QueryInterface(IPrivateKey,PrivateKey));
   xmldsig.store := CertStoreMem;

   dsigKey := xmldsig.createKeyFromCSP(PrivateKey.ProviderType, PrivateKey.ProviderName, PrivateKey.ContainerName, 0);
   if (dsigKey = nil) then
      raise EACBrNFeException.Create('Erro ao criar a chave do CSP.');

   signedKey := xmldsig.sign(dsigKey, $00000002);
   if (signedKey <> nil) then
    begin
      XMLAssinado := xmldoc.xml;
      XMLAssinado := StringReplace( XMLAssinado, #10, '', [rfReplaceAll] ) ;
      XMLAssinado := StringReplace( XMLAssinado, #13, '', [rfReplaceAll] ) ;
      PosIni := Pos('<SignatureValue>',XMLAssinado)+length('<SignatureValue>');
      XMLAssinado := copy(XMLAssinado,1,PosIni-1)+StringReplace( copy(XMLAssinado,PosIni,length(XMLAssinado)), ' ', '', [rfReplaceAll] ) ;
      PosIni := Pos('<X509Certificate>',XMLAssinado)-1;
      PosFim := DFeUtil.PosLast('<X509Certificate>',XMLAssinado);

      XMLAssinado := copy(XMLAssinado,1,PosIni)+copy(XMLAssinado,PosFim,length(XMLAssinado));
    end
   else
      raise EACBrNFeException.Create('Assinatura Falhou.');

   if xmlHeaderAntes <> '' then
   begin
      I := pos('?>',XMLAssinado) ;
      if I > 0 then
       begin
         xmlHeaderDepois := copy(XMLAssinado,1,I+1) ;
         if xmlHeaderAntes <> xmlHeaderDepois then
            XMLAssinado := StuffString(XMLAssinado,1,length(xmlHeaderDepois),xmlHeaderAntes) ;
       end
      else
         XMLAssinado := xmlHeaderAntes + XMLAssinado ;
   end ;

   dsigKey   := nil;
   signedKey := nil;
   xmldoc    := nil;
   xmldsig   := nil;

   Result := True;
end;
Para carregar o arquivo:

 

var
  vAssinada : AnsiString;
  FMsg : AnsiString;
  ArquivoFormatoXML : TStringList;
begin
  OpenDialog1.Title := 'Selecione a NFE';
  OpenDialog1.DefaultExt := '*-nfe.XML';
  OpenDialog1.Filter := 'Arquivos NFE (*-nfe.XML)|*-nfe.XML|Arquivos XML (*.XML)|*.XML|Todos os Arquivos (*.*)|*.*';
  OpenDialog1.InitialDir := ACBrNFe1.Configuracoes.Geral.PathSalvar;
  if OpenDialog1.Execute then
  begin
    ArquivoFormatoXML := TStringList.Create;
    try
       ArquivoFormatoXML.LoadFromFile(OpenDialog1.FileName);
       ArquivoFormatoXML.Text := StringReplace( ArquivoFormatoXML.Text, #10, '', [rfReplaceAll] ) ;
       ArquivoFormatoXML.Text := StringReplace( ArquivoFormatoXML.Text, #13, '', [rfReplaceAll] ) ;
       ArquivoFormatoXML.Text := StringReplace( ArquivoFormatoXML.Text, #$D#$A, '', [rfReplaceAll] ) ;

       if not(NotaUtil.Assinar(ArquivoFormatoXML.Text, ACBrNFe1.Configuracoes.Certificados.GetCertificado , vAssinada, FMsg)) then
                                raise EACBrNFeException.Create('Falha ao assinar');
       MemoAssinado.Clear;
       MemoAssinado.Lines.Add(vAssinada);
    finally
       ArquivoFormatoXML.Free;
    end;
  end;
end;
O arquivo a ser assinado deve começar a partir da chave <DIe... até o </DIe>, ou seja, depois de assinar que vc deverá completar o arquivo com as informações de envio, o arquivo anexo está assinado e foi validado com sucesso no site https://www.receita.fazenda.gov.br/Aplicacoes/SSL/ATBHE/assinadoc/ValidadorAssinaturas.app/valida.aspx

teste_die.xml

djsystem-logo.png
 youtube.png facebook.png instagram.png linkedin.png
André Ferreira de Moraes | Analista de Sistemas
www.djsystem.com.br | www.djpdv.com.br
www.tefhouse.com.br | www.xpos.com.br
  • Membros Pro
Postado (editado)

André, usando esta rotina quando tentar carregar o arquivo na instrução:    If not xmldoc.loadXML(XML) then

      raise EACBrNFeException.Create('Não foi possível carregar o arquivo XML:'+XML);
 
Ela retorna Exceção. Em anexo estou colocando um dos vários arquivos que usei para teste.

DIe1401507761.xml

Editado por rommaraujo
  • Membros Pro
Postado

André encontrei a falha nesta rotina. O seu aruiqvo de teste não possui a tag <enviDIe> e por consequência o seu fechamento <enviDIe>. quando invoco o método Assinar que ele acrescenta a estrutura de assinatura ao final ele devolve apenas a </DIe>, faltando acrescentar a tag </enviDIe>.

 

Feito isto a rotina funcionou perfeitamente. Se possível seria interessante acrescentar na rotina de assinatura o tipo DIe em futuras versões do ACBr, se este tipo de documento existir para outros estados além do Amazonas. 

 

Muito obrigado pela colaboração. É sempre bom saber que podemos contar com a cooperação de profissionais competentes.

 

Cordialmente,

 

Rômulo Araújo.

  • Este tópico foi criado há 3773 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.