Usando classe para assinar nfse - São Paulo

Projeto hbNFe (Nota Fiscal Eletronica/Danfe) para [x]Harbour

Moderador: Moderadores

malcarli
Usuário Nível 3
Usuário Nível 3
Mensagens: 239
Registrado em: 20 Ago 2015 18:14
Localização: marilia/sp

Usando classe para assinar nfse - São Paulo

Mensagem por malcarli »

Mestre,

ShellExecute(0, [open], [AssinaRPS.EXE],,, 1)

este trecho o amigo Eduardo, fez esse executável em c# para assinar a tag assinatura, como não consegui nenhuma informação de como converter para harbour estou usando por eqto este executável externamente.

Já juntei um pouco conforme o código abaixo

Código: Selecionar todos


#include "hbclass.ch"

*#define WS_NFE_STATUSSERVICO         17   /// não descobri onde usar

#define WS_CANCELAMENTONFE           1
#define WS_ENVIOLOTERPS              2
#define WS_ENVIORPS                  3    /// acrescentei
#define WS_CONSULTARPS               4    /// acrescentei

#define WS_AMBIENTE_HOMOLOGACAO      "2"  /// sp, foz não tem mais o ambiente de homologação
#define WS_AMBIENTE_PRODUCAO         "1"

#ifndef XML_UTF8
   #define XML_UTF8                     [<?xml version="1.0" encoding="UTF-8"?>]
#endif

CREATE CLASS NfseClasse   //// mudei nome pois estava conflitando com a sefazclass  

   /* configuração */
   VAR    cAmbiente        INIT WS_AMBIENTE_PRODUCAO
   VAR    cUF              INIT "SP"                    // Modificada conforme método
   VAR    cCertificado     INIT ""                      // Nome do certificado
   VAR    nTempoEspera     INIT 7                       // intervalo entre envia lote e consulta recibo
   /* XMLs de cada etapa */
   VAR    cXmlDocumento    INIT ""                      // O documento oficial, com ou sem assinatura, depende do documento
   VAR    cXmlEnvio        INIT ""                      // usado pra criar/complementar XML do documento
   VAR    cXmlSoap         INIT ""                      // XML completo enviado pra Sefaz, incluindo informações do envelope
   VAR    cXmlRetorno      INIT "Erro Desconhecido"     // Retorno do webservice e/ou rotina
   VAR    cXmlProtocolo    INIT ""                      // XML protocolo (obtido no consulta recibo e/ou envio de outros docs)
   VAR    cXmlAutorizado   INIT ""                      // XML autorizado, caso tudo ocorra sem problemas
   VAR    cStatus          INIT Space(3)                // Status obtido da resposta final da Fazenda
   /* uso interno */
   VAR    cSoapService     INIT ""                      // webservice Serviço
   VAR    cSoapAction      INIT ""                      // webservice Action
   VAR    cSoapURL         INIT ""                      // webservice Endereço

*** acrescentei
   VAR    lComUri          INIT .F.                     // Não tem tag cUri
   VAR    cPassword        INIT ""                      // Senha de arquivo PFX
   VAR    cMotivo          INIT ""                      // Motivo constante no Recibo
   VAR    nCodigoMunicipio INIT 3550308                 // São Paulo

   METHOD EnvioLoteRPS( cXml, cCertificado, cAmbiente )
 ***** acrescentei
   METHOD EnvioRPS( cXml, cCertificado, cAmbiente )
   METHOD CancelamentoRPS( cXml, cCertificado, cAmbiente )
   METHOD ConsultaRPS( cXml, cCertificado, cAmbiente )

   /* Uso interno */
   METHOD Setup( cCertificado, cAmbiente, nWsServico )
   METHOD XmlSoapEnvelope()
   METHOD XmlSoapPost()
   METHOD MicrosoftXmlSoapPost()
***** acrescentei
   METHOD AssinaXml(cXml)
   METHOD Gera_Chave_SHA1(cString)

   ENDCLASS

** alterei foz máximo 20 rps por lote
METHOD EnvioLoteRPS( cXml, cCertificado, cAmbiente ) CLASS NfseClasse

   ::Setup( cCertificado, cAmbiente, WS_ENVIOLOTERPS )
   ::cXmlEnvio := ::AssinaXml( cXml )
   ::cXmlEnvio := ::cXmlDocumento
   ::XmlSoapPost()

   RETURN ::cXmlRetorno

** acrescentei
METHOD EnvioRPS( cXml, cCertificado, cAmbiente ) CLASS NfseClasse
   ::Setup( cCertificado, cAmbiente, WS_ENVIORPS )
   ::cXmlEnvio := ::AssinaXml( cXml )
   ::cXmlEnvio := ::cXmlDocumento
   ::XmlSoapPost()

   RETURN ::cXmlRetorno

** acrescentei
METHOD CancelamentoRPS( cXml, cCertificado, cAmbiente ) CLASS NfseClasse
   ::Setup( cCertificado, cAmbiente, WS_CANCELAMENTONFE )

   ::cXmlEnvio := ::AssinaXml( cXml )
   ::cXmlEnvio := ::cXmlDocumento
   ::XmlSoapPost()

   RETURN ::cXmlRetorno

** acrescentei
METHOD ConsultaRPS( cXml, cCertificado, cAmbiente ) CLASS NfseClasse
   ::Setup( cCertificado, cAmbiente, WS_CONSULTARPS )
   ::cXmlEnvio := ::AssinaXml( cXml )
   ::cXmlEnvio := ::cXmlDocumento
   ::XmlSoapPost()

   RETURN ::cXmlRetorno

METHOD Setup( cCertificado, cAmbiente, nWsServico ) CLASS NfseClasse
   LOCAL nPos, aSoapList

   If ::nCodigoMunicipio == 3550308 // sp
      aSoapList := { ;
                   { WS_CANCELAMENTONFE, "CancelamentoNFe", "http://www.prefeitura.sp.gov.br/nfe/ws/cancelamentoNFe", "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx" }, ;
                   { WS_ENVIOLOTERPS   , "EnvioLoteRPS"   , "http://www.prefeitura.sp.gov.br/nfe/ws/envioLoteRPS"   , "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx" }, ;
                   { WS_ENVIORPS       , "EnvioRPS"       , "http://www.prefeitura.sp.gov.br/nfe/ws/envioRPS"       , "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx" }, ;
                   { WS_CONSULTARPS    , "ConsultaLote"   , "http://www.prefeitura.sp.gov.br/nfe/ws/consultaLote"   , "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx" } }
   ElseIf ::nCodigoMunicipio == 4108304 // foz do iguaçu
      aSoapList := { ;
                   { WS_CANCELAMENTONFE, "CancelamentoNFSE"           , "http://tempuri.org/CancelamentoNFSE"           , "http://nfse.pmfi.pr.gov.br/nfsews/nfse.asmx" }, ;
                   { WS_ENVIOLOTERPS   , "EnviaLotesParaProcessamento", "http://tempuri.org/EnviaLotesParaProcessamento", "http://nfse.pmfi.pr.gov.br/nfsews/nfse.asmx" }, ;
                   { WS_ENVIORPS       , "RecebeLoteRPS"              , "http://tempuri.org/RecebeLoteRPS"              , "http://nfse.pmfi.pr.gov.br/nfsews/nfse.asmx" }, ;
                   { WS_CONSULTARPS    , "ConsultarLoteRPS"           , "http://tempuri.org/ConsultarLoteRPS"           , "http://nfse.pmfi.pr.gov.br/nfsews/nfse.asmx" } }
   Endif

*  sp e foz, apesar de ter endereço de homologação, estão desativados, conforme o suporte dos mesmos

   ::cCertificado := iif( cCertificado == Nil, ::cCertificado, cCertificado )
   ::cAmbiente    := iif( cAmbiente == Nil, ::cAmbiente, cAmbiente )   

   IF nWsServico == Nil
      RETURN Nil
   ENDIF
   IF ( nPos := hb_AScan( aSoapList, { | oElement | oElement[ 1 ] == nWsServico } ) ) != 0
      ::cSoapService := aSoapList[ nPos, 2 ]
      ::cSoapAction  := aSoapList[ nPos, 3 ]
      ::cSoapURL     := aSoapList[ nPos, 4 ]

   ENDIF
   RETURN Nil

METHOD XmlSoapPost() CLASS NfseClasse

   DO CASE
   CASE Empty( ::cSoapURL )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de webservice"
      RETURN Nil
   CASE Empty( ::cSoapService )
      ::cXmlRetorno := "Erro SOAP: Não há nome do serviço"
      RETURN Nil
   CASE Empty( ::cSoapAction )
      ::cXmlRetorno := "Erro SOAP: Não há endereço de SOAP Action"
      RETURN Nil
   ENDCASE
   ::XmlSoapEnvelope()
   ::MicrosoftXmlSoapPost()
   IF Upper( Left( ::cXmlRetorno, 4 ) )  == "ERRO"
      RETURN Nil
   ENDIF

   RETURN Nil

METHOD XmlsoapEnvelope() CLASS NfseClasse
   ::cXmlsoap    := XML_UTF8
   ::cXmlsoap    += [<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ]
   ::cXmlsoap    +=       [xmlns:xsd="http://www.w3.org/2001/XMLSchema" ]
   ::cXmlsoap    +=       [xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">]
   ::cXmlSoap    +=    [<soap12:Body>]

   If ::nCodigoMunicipio == 3550308 // sp
      ::cXmlSoap    +=       [<] + ::cSoapService + [Request xmlns="http://www.prefeitura.sp.gov.br/nfe">]
      ::cXmlSoap    +=          [<VersaoSchema>1</VersaoSchema>]
      ::cXmlSoap    +=          [<MensagemXML>]
      ::cXmlSoap    +=             "<![CDATA[ " + ::cXmlEnvio + " ]]>"
      ::cXmlSoap    +=          [</MensagemXML>]
      ::cXmlSoap    +=       [</] + ::cSoapService + [Request>]
   ElseIf ::nCodigoMunicipio == 4108304 // foz do iguaçu
      ::cXmlSoap    +=       [<] + ::cSoapService + [ xmlns="http://tempuri.org/">]
      ::cXmlSoap    +=             [<xml>] + ::cXmlEnvio + [</xml>]
      ::cXmlSoap    +=       [</] + ::cSoapService + [>]
   Endif

   ::cXmlSoap    +=   [</soap12:Body>]
   ::cXmlSoap    += [</soap12:Envelope>]
   RETURN Nil

METHOD MicrosoftXmlSoapPost() CLASS NfseClasse

   LOCAL oServer, nCont, cRetorno
   LOCAL cSoapAction

   cSoapAction := ::cSoapAction
   BEGIN SEQUENCE WITH __BreakBlock()
      ::cXmlRetorno := "Erro: Criando objeto MSXML2.ServerXMLHTTP"
      oServer := win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
      ::cXmlRetorno := "Erro: No uso do objeto MSXML2.ServerXmlHTTP"
      IF ::cCertificado != Nil
         oServer:setOption( 3, "CURRENT_USER\MY\" + ::cCertificado )
      ENDIF
      ::cXmlRetorno := "Erro: Na conexão com webservice " + ::cSoapURL
      oServer:Open( "POST", ::cSoapURL, .F. )
      oServer:SetRequestHeader( "SOAPAction", cSoapAction )
      oServer:SetRequestHeader( "Content-Type", "application/soap+xml; charset=utf-8" )
      oServer:Send( ::cXmlSoap )
      oServer:WaitForResponse( 500 )
      cRetorno := oServer:ResponseBody
      IF ValType( cRetorno ) == "C"
         ::cXmlRetorno := cRetorno
      ELSEIF cRetorno == Nil
         ::cXmlRetorno := "Erro: Sem retorno do webservice"
      ELSE
         ::cXmlRetorno := ""
         FOR nCont = 1 TO Len( cRetorno )
            ::cXmlRetorno += Chr( cRetorno[ nCont ] )
         NEXT
      ENDIF
   ENDSEQUENCE
   IF "<soap:Body>" $ ::cXmlRetorno .AND. "</soap:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soap:Body" ) // hb_UTF8ToStr()
   ELSEIF "<soapenv:Body>" $ ::cXmlRetorno .AND. "</soapenv:Body>" $ ::cXmlRetorno
      ::cXmlRetorno := XmlNode( ::cXmlRetorno, "soapenv:Body" ) // hb_UTF8ToStr()
   ELSE
      ::cXmlRetorno := "Erro SOAP: XML retorno não contém soapenv:Body " + ::cXmlRetorno
   ENDIF

   RETURN Nil

**************** acrescentei 
METHOD AssinaXml(cXml) CLASS NfseClasse
   ::cXmlDocumento:= cXml
   ::cXmlRetorno  := CapicomAssinaXml( @::cXmlDocumento, ::cCertificado,,::cPassword, ::lComUri )

   IF ::cXmlRetorno != "OK"
      ::cStatus := "999"
      ::cMotivo := ::cXmlRetorno
      ::cXmlRetorno := [<erro text="] + "*erro* " + ::cXmlRetorno + ["</erro>]
   ENDIF
RETURN ::cXmlRetorno

METHOD Gera_Chave_SHA1(cString) Class NfseClasse
   Local nHandle:= Fcreate([StringParaAssinar.txt]), cRet:= []

*  Fwrite(nHandle, ::ohbNFe:cSerialCert + hb_OsNewLine() + cString)
   Fwrite(nHandle, Upper([4b8db3b9d22a50b5]) + hb_OsNewLine() + cString)
   Fclose(nHandle)
   ShellExecute(0, [open], [AssinaRPS.EXE],,, 1)

   cRet:= Hb_Memoread([StringAssinada.txt])
   cRet:= Strtran(cRet, CHR(13), [])
   cRet:= Strtran(cRet, CHR(10), [])

   Ferase([StringParaAssinar.txt])
   Ferase([StringAssinada.txt])
Return(cRet)

********************* Retira Acentos e Letras de uma String ********************
Function fRetiraAcento(cStr)
   Local aLetraCAc:= {[Á],[À],[Ä],[Ã],[Â],[É],[È],[Ë],[Ê],[&],[Í],[Ì],[Ï],[Î],[Ó],[Ò],[Ö],[Õ],[Ô],[Ú],[Ù],[Ü],[Û],[Ç],[Ñ],[Ý],[á],[à],[ä],[ã],[â],[é],[è],[ë],[ƒ],[ê],[í],[ì],[ï],[î],[ó],[ò],[ö],[õ],[ô],[ú],[ù],[ü],[û],[ç],[ñ],[ý],[ÿ],[º] ,[ª] ,[‡],[Æ],[¡],[£],[ÿ],[ ],[á],[ ] ,[ ],[ ],[‚],[ˆ],[“],[¢],[…],[°],[A³],[A§],[Au],[Ai],[A©],[Ao.]}
   Local aLetraSAc:= {[A],[A],[A],[A],[A],[E],[E],[E],[E],[E],[I],[I],[I],[I],[O],[O],[O],[O],[O],[U],[U],[U],[U],[C],[N],[Y],[a],[a],[a],[a],[a],[e],[e],[e],[a],[e],[i],[i],[i],[i],[o],[o],[o],[o],[o],[u],[u],[u],[u],[c],[n],[y],[y],[o.],[a.],[c],[a],[i],[u],[a],[a],[a],[E ],[a],[ ],[e],[e],[o],[o],[a],[],[o],[c],[a],[a],[e],[u]}, i
   hb_Default(@cStr, [])

   For i:= 1 To Len(aLetraCAc)
       cStr:= StrTran(cStr, aLetraCAc[i], aLetraSAc[i])
   Next
Return (cStr)
********************* Fim da Função Retira Acentos e Letras de uma String ******
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Usando classe para assinar nfse - São Paulo

Mensagem por JoséQuintas »

malcarli escreveu: como não consegui nenhuma informação de como converter para harbour estou usando por eqto este executável externamente.
Geralmente é assinado um bloco do XML.
No caso da NFE, é o bloco com Id=

Lembro no caso do lote de RPS, que eram duas (ou mais) assinaturas:
CADA nota tinha assinatura, e no final o lote também era assinado.
Deixei a rotina de assinatura preparada pra isso, mas... não está automática, teria que assinar cada nota, juntar tudo, e depois assinar o lote.
Nunca recebi retorno de quem precisou, se tudo deu certo.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
malcarli
Usuário Nível 3
Usuário Nível 3
Mensagens: 239
Registrado em: 20 Ago 2015 18:14
Localização: marilia/sp

Usando classe para assinar nfse - São Paulo

Mensagem por malcarli »

Mestre, não é essa situação. Estou enviando somente uma nota por vez.

Veja o exemplo da tag assinatura:

é uma string montada com algumas informações:

37925504A 00000000102620230321TNN00000000000010000000000000000002919232332624000100

depois de assindada vira isso

<Assinatura>dhfhixJYMyGgZWUqH7zPvboxs9NbcKkyyKO46qKCXbT05HGyZkoyR7KdTX7KJ4m3YljMIzq00hZ2fjUC0HRdnBnDzGYllgzMTew+faUjDCwJlG/0y8ZfVkIFOOF2tsWklwu32lJ2vhhdtqHTTERFJUs0QItIsZz/jdq1BCYEXlCle7zF2ZZhkUzCIbo7U4SjAmNlx8vcsEJCwVGAlML3dEArbYgi0GfK58LsWJEBwK8/Iont24kqvaaQiahyJWXSLPrK/L4u2bEwO37Xp/wo8H8GAFIiNLMLvBby3zJlUu8IiuJm8SP4bg+A4hB0oJ0YuUH6ra52RFk59IRrq/1G2w==</Assinatura>


A tag assinatura é composta pelo seguinte conteúdo:

************************COLOCAR AQUI A CHAVE PARA COMPARAR PARA VER SE ESTÁ CERTO*********************

2º - Converta a cadeia de caracteres ASCII para bytes.
3º - Gere o HASH (array de bytes) utilizando SHA1.
4º - Assine o HASH (array de bytes) utilizando RSA-SHA1.
ATENÇÃO! Na maioria das linguagens de programação, os passos 3 e 4 são feitos através de uma única função. Verifique a documentação de sua linguagem para evitar assinar um hash de um hash.
37925504A 00000000085320230201TNN00000000000010000000000000000002919112345678000199
58402454NF 00000000001620180607TNN00000000001000000000000000000003654214675166000112
31000000OL03 00000000000120070103TNN00000000205000000000000050000002658100013167474254
+-------+----+-----------+-------++++--------------+--------------+----++-------------++-------------+
123456781234512345678901212345678111123456789012345123456789012345123451123456789012341123456789012341
+-------+----+-----------+-------++++--------------+--------------+----++-------------++-------------+
| | | | |||| | | || || |
A B C D EFGH I J KL MN O
+------------------------------------------+-------------+
| A-Inscrição Municipal do Prestador | 8 dígitos |
| B-Série do RPS | 5 diditos |
| C-Número do RPS | 12 dígitos |
| D-Data de Emissão do RPS | 8 dígitos |
| E-Tipo de Tributação do RPS | 1 dígito |
| F-Status do RPS | 1 dígito |
| G-ISS Retido | 1 dígito |
| H-Valor dos Serviços | 15 dígitos |
| I-Valor das deduções | 15 dígitos |
| J-Código do Serviço Prestado | 5 dígitos |
| K-Indicador de CPF/CNPJ do tomador | 1 dígito |
| L-CPF/CNPJ do tomador | 14 dígitos |
| M-Indicador de CPF/CNPJ do Intermediário | 1 dígito |
| N-CPF/CNPJ do Intermediário | 14 dígitos |
| O-ISS Retido Intermediário | 1 dígito |
+------------------------------------------+-------------+
| TOTAL DE DÍGITOS | 102 dígitos |
+------------------------------------------+-------------+

2º - Converta a cadeia de caracteres ASCII para bytes.
3º - Gere o HASH (array de bytes) utilizando SHA1.
4º - Assine o HASH (array de bytes) utilizando RSA-SHA1.
ATENÇÃO! Na maioria das linguagens de programação, os passos 3 e 4 são feitos através de uma única função. Verifique a documentação de sua linguagem para evitar assinar um hash de um hash.
Responder