Assinar texto/arquivo com certificado digital pelo windows

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

Avatar do usuário
rochinha
Administrador
Administrador
Mensagens: 4664
Registrado em: 18 Ago 2003 20:43
Localização: São Paulo - Brasil
Contato:

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por rochinha »

Amiguinhos,

ao testar me faltou as funções:


HB_SYMBOL_UNUSED()
CriptCapiCom()
DescriptCapiCom()
OPS! LINK QUEBRADO? Veja ESTE TOPICO antes e caso não encontre ENVIE seu email com link do tópico para [url=mailto://fivolution@hotmail.com]fivolution@hotmail.com[/url]. Agradecido.

@braços : ? )

A justiça divina tarda mas não falha, enquanto que a justiça dos homens falha porque tarda.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por JoséQuintas »

Só hoje testei isso.
O mais próximo que cheguei foi alterando a rotina de Hash, mas o digest value não bate.

Sha1, porque está no manual da NFE.
O resto peguei numa rotina da NET.

Código: Selecionar todos

   IF nAlgorithm = NIL
      nAlgorithm := CAPICOM_HASH_ALGORITHM_SHA1 // 256
   ENDIF

   oUtil    := win_OleCreateObject( "CAPICOM.Utilities" )
   oCapicom := win_OleCreateObject( "CAPICOM.HashedData.1" )
   oCapicom:Algorithm := nAlgorithm
   oCapicom:Hash( cData )

   RETURN oUtil:Base64Encode( outil:HexToBinary( oCapicom:Value ) )
DigestValue PwA/AD8APwA/AD8APwA/AD8APwA=

DigestValue d/H7RrkNBl1a159pNWFgKKycQ9k=
Se o digestvalue não bate... nem adianta assinar, porque é o digestvalue que é assinado.
Falta algum detalhe referente ao XML.
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/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por JoséQuintas »

Inclusive tem uma parte, não chega a ser errada, mas é só pra complicar:
Signer := CreateObject("CAPICOM.Signer.2") //versao 2
Signer:Certificate := cert
CertBase64 := Signer:Certificate:Export(CAPICOM_ENCODE_BASE64)
a rotina acima coloca o certificado no "assinador", pra extrair o certificado....

Mais prático eliminar isso e usar direto o certificado:
Cert:Export( CAPICOM_ENCODE_BASE64 )
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/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por JoséQuintas »

A rotina alterada:

Código: Selecionar todos

/*
https://pctoledo.org/forum/viewtopic.php?f=43&t=18664
*/

#define __TESTE__

#include "sefaz_capicom.ch"
#include "hbclass.ch"
#ifdef __TESTE__

PROCEDURE TesteCapicom

   LOCAL cTexto, cSignatureValue, cDigestValue, cPublicKey

   cTexto    := "texto a ser gerado hash, no caso de arquivo, carregue o arquivo e passe para esta funcao"

   cDigestValue    := CapicomClass():HashData( cTexto )
   cSignatureValue := CapicomClass():Sign( cDigestValue, , , @cPublicKey )

   ?
   ? cDigestValue == CapicomClass():VerifySignature( cSignatureValue )
   ? IsValidSignatureCapicom( cDigestValue, cSignatureValue )
   ? cPublicKey

   WAIT

   RETURN
#endif

FUNCTION IsValidSignatureCapicom( cDigestValue, cSignatureValue )

   IF cDigestValue = NIL .OR. cSignatureValue = NIL
      RETURN .F.
   ENDIF

   RETURN CapicomClass():VerifySignature( cSignatureValue ) == cDigestValue

CREATE CLASS CapicomClass

   METHOD SelectCertificate()
   METHOD VerifySignature( cSignedData )
   METHOD HashData( cData, nAlgorithm )
   METHOD PublicKey( oCAPICOMCert )
   METHOD Sign( cDigestValue, oCAPICOMcert, nEncode, cPublicKey )

   END CLASS

METHOD SelectCertificate() CLASS CapicomClass

   LOCAL oCapicom, oCertificate

   oCapicom:= win_OleCreateObject( "CAPICOM.Store" )
   oCapicom:Open( CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY )
   BEGIN SEQUENCE WITH { || __BreakBlock() }
      oCertificate := oCapicom:Certificates:Select( "Selecione um certificado digital", "Algoritmo de Assinatura SHA256RSA" )
   END SEQUENCE
   IF oCapicom:Certificates:Count() == 0
      RETURN NIL
   ENDIF

   RETURN oCertificate:Item( 1 )

METHOD VerifySignature( cSignedData ) CLASS CapicomClass

   LOCAL oCapicom

   IF cSignedData == NIL
      RETURN NIL
   ENDIF

   oCapicom := win_OleCreateObject( "CAPICOM.SignedData.1" )
   oCapicom:Verify( cSignedData, .F., CAPICOM_VERIFY_SIGNATURE_ONLY )

   RETURN oCapicom:Content

METHOD HashData( cData, nAlgorithm ) CLASS CapicomClass

   LOCAL oCapicom, oUtil

   IF cData = NIL
      cData := DToS( Date() ) + Time()
   ENDIF

   IF nAlgorithm = NIL
      nAlgorithm := CAPICOM_HASH_ALGORITHM_SHA1 // 256
   ENDIF

   oUtil    := win_OleCreateObject( "CAPICOM.Utilities" )
   oCapicom := win_OleCreateObject( "CAPICOM.HashedData.1" )
   oCapicom:Algorithm := nAlgorithm
   oCapicom:Hash( cData )

   RETURN oUtil:Base64Encode( outil:HexToBinary( oCapicom:Value ) )
   // RETURN oCapicom:Value

METHOD PublicKey( oCapicomCert ) CLASS CapicomClass

   LOCAL cPublicKey

   //oCapicom := win_OleCreateObject( "CAPICOM.Signer.2" )
   //oCapicom:Signer:Certificate := oCAPICOMCert
   //oCapicom:Signer:Options := CAPICOM_CERTIFICATE_INCLUDE_CHAIN_EXCEPT_ROOT
   cPublicKey := StrTran( /* oCapicom:Certificate:*/ oCapicomCert:Export( CAPICOM_ENCODE_BASE64 ), Chr(13) + Chr(10), "" )

   RETURN cPublicKey

METHOD Sign( cDigestValue, oCAPICOMCert, nEncode, cPublicKey ) CLASS CapicomClass

   LOCAL oCAPICOMSignedData, oCAPICOMSigner, oCAPICOMTimeStamp, cSignature

   IF cDigestValue = NIL
      RETURN NIL
   ENDIF

   IF nEncode = NIL
      nEncode := CAPICOM_ENCODE_BASE64
   ENDIF

   oCAPICOMSigner := win_OleCreateObject( "CAPICOM.Signer.2" ) // versao 2

   IF oCAPICOMCert = NIL
      oCAPICOMCert := ::SelectCertificate()
      IF oCAPICOMCert = NIL
         RETURN NIL
      ENDIF
      oCAPICOMSigner:Certificate := oCAPICOMcert
   ELSE
      oCAPICOMSigner:Certificate := oCAPICOMcert:DefaultInterface
   ENDIF

   IF ! ( oCAPICOMSigner:Certificate:HasPrivateKey ;
         .AND. DToS( oCAPICOMSigner:Certificate:ValidFromDate ) <= DToS( Date() ) ;
         .AND. DToS( oCAPICOMSigner:Certificate:ValidToDate ) >= DToS( Date() ) )
      RETURN NIL
   ENDIF

   oCAPICOMSigner:Options := CAPICOM_CERTIFICATE_INCLUDE_CHAIN_EXCEPT_ROOT
   cPublicKey             := StrTran( oCAPICOMSigner:Certificate:Export( CAPICOM_ENCODE_BASE64 ), Chr(13) + Chr(10), "" )

   oCAPICOMTimeStamp := win_OleCreateObject( "CAPICOM.Attribute" )
   oCAPICOMTimeStamp:Name  := CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME
   oCAPICOMTimeStamp:Value := hb_DateTime()

   oCAPICOMSigner:AuthenticatedAttributes:Add( oCAPICOMTimeStamp )

   oCAPICOMSignedData := win_OleCreateObject( "CAPICOM.SignedData.1" )
   oCAPICOMSignedData:Content := cDigestValue

   // segundo parametro falso, apenas retona assinatura do texto, não inclui no texto
   cSignature := oCAPICOMSignedData:Sign( oCAPICOMSigner, .F., nEncode )

   RETURN cSignature
E a rotina que usei pra teste:

Código: Selecionar todos

PROCEDURE Main

   LOCAL cXml, cDigestValue, cSignatureValue, cXml2, cPublicKey

   cPublicKey      := CapicomClass():PublicKey( CapicomCertificado( "CORDEIRO" ) )
   cXml            := MemoRead( "X.XML" )
   cXml2           := cXml
   cXml            := XmlNode( cXml, "infNFe", .T. )
   cDigestValue    := CapicomClass():HashData( cXml )
   cSignatureValue := CapicomClass():Sign( cDigestValue, , , @cPublicKey )

   CapicomAssinaXml( @cXml2, "CORDEIRO" )
   hb_MemoWrit( "d:\temp\assinado.xml", cXml2 )

   ? "DigestValue", cDigestValue
   ? "DigestValue", XmlNode( cXml2, "DigestValue" )
   ? cDigestValue == XmlNode( cXml2, "DigestValue" )
   ? cSignatureValue == XmlNode( cXml2, "SignatureValue" )
   ? cPublicKey == XmlNode( cXml2, "X509Certificate" )
   Inkey(0)

   RETURN
Compilei usando isto:

Código: Selecionar todos

hbmk2 test.prg \cdrom\fontes\integra\sefazclass\drafts\assinaturacapicom.prg -otest josequintas.hbc -gtwin
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/
Avatar do usuário
sygecom
Administrador
Administrador
Mensagens: 7131
Registrado em: 21 Jul 2006 10:12
Localização: Alvorada-RS
Contato:

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por sygecom »

Ze,
Tenho uma sugestão. Depois de usar o METODO OPEN() da CAPICOM.Store, seria bom fechar o mesmo com: oCapicom:close(), isso evita corromper o certificado A3 que fica gravado nos pendrive/cartão, para certificado A1, se acontecer é só instalar de novo.

Código: Selecionar todos

oCapicom:= win_OleCreateObject( "CAPICOM.Store" )
oCapicom:Open( CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY )
BEGIN SEQUENCE WITH { || __BreakBlock() }
    oCertificate := oCapicom:Certificates:Select( "Selecione um certificado digital", "Algoritmo de Assinatura SHA256RSA" )
END SEQUENCE
oCapicom:close()

https://msdn.microsoft.com/en-us/librar ... 2147217396
Leonardo Machado
xHarbour.org + Hwgui + PostgreSql
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Assinar texto/arquivo com certificado digital pelo windows

Mensagem por JoséQuintas »

Lembro de ter gerado erro no Close(), por isso deixei de usar.
Uso assim há anos e nunca tive problema.
Mas vou fazer novos testes.
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/
Responder