Ok.
Esta parte é legal, descomplicando um pouco o METHOD de assinatura, que aparentemente não é usada, já que usa programa externo...
METHOD Assina_XML() CLASS NFSE
LOCAL oDOMDoc, oXmldsig, oCert, oStoreMem, dsigKey, signedKey
LOCAL aRETORNO := Hash()
LOCAL cXML, cXMLSig
LOCAL PosIni, PosFim, nP, nResult
LOCAL nHandle
aRETORNO[ 'STATUS' ] := .F.
aRETORNO[ 'MSG' ] := ''
IF ::Xml = NIL .OR. Empty( ::Xml )
aRETORNO[ 'MSG' ] := 'Arquivo XML com o registro do cabeçalho e RPS não informado.'
ENDIF
cXML := MemoRead( ::Xml )
FErase( ::Xml )
cXML := StrTran( cXML, '<Signature></Signature>', '<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="">' + ;
'<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>' )
// Inicializa o objeto do DOMDocument
BEGIN SEQUENCE WITH __BreakBlock()
oDOMDoc := win_oleCreateObject( _MSXML2_DOMDOCUMENT )
RECOVER
aRETORNO[ 'MSG' ] := 'Nao foi possÃvel carregar ' + _MSXML2_DOMDOCUMENT
RETURN aRETORNO
ENDSEQUENCE
oDOMDoc:async = .F.
oDOMDoc:resolveExternals := .F.
oDOMDoc:validateOnParse = .T.
oDOMDoc:preserveWhiteSpace = .T.
// inicializa o objeto do MXDigitalSignature
BEGIN SEQUENCE WITH __BreakBlock()
oXmldsig := win_oleCreateObject( _MSXML2_MXDIGITALSIGNATURE )
RECOVER
aRETORNO[ 'MSG' ] := 'Nao foi possÃvel carregar ' + _MSXML2_MXDIGITALSIGNATURE
RETURN aRETORNO
ENDSEQUENCE
nHandle := FCreate( ::ohbNFe:pastaEnvRes + "\edunfs.xml" )
FWrite( nHandle, cXML )
FClose( nHandle )
// carrega o arquivo XML para o DOM
oDOMDoc:LoadXML( cXML )
IF oDOMDoc:parseError:errorCode <> 0
aRETORNO[ 'MSG' ] := ' Assinar: Não foi possÃvel carregar o documento pois ele não corresponde ao seu Schema' + hb_Eol() + ;
' Linha: ' + Str( oDOMDoc:parseError:line ) + hb_eol() + ;
' Caractere na linha: ' + Str( oDOMDoc:parseError:linepos ) + hb_eol() + ;
' Causa do erro: ' + oDOMDoc:parseError:reason + hb_eol() + ;
' code: ' + Str( oDOMDoc:parseError:errorCode )
RETURN aRETORNO
ENDIF
// Localiza as assinaturas no XML
oDOMDoc:SETPROPERTY( 'SelectionNamespaces', "xmlns:ds='http://www.w3.org/2000/09/xmldsig#'" )
oXmldsig:signature := oDOMDoc:selectSingleNode( './/ds:Signature' )
IF ( oXmldsig:signature = nil )
aRETORNO[ 'MSG' ] := 'É preciso carregar o template antes de assinar.'
RETURN aRETORNO
ENDIF
// carrega o objeto do certificado digital
oCert := ::ohbNFe:pegaObjetoCertificado( ::ohbNFe:cSerialCert )
IF oCert == Nil
aRETORNO[ 'MSG' ] := 'Certificado não encontrado, Favor revisar a instalação do Certificado'
RETURN aRETORNO
ENDIF
// cria o objeto de Store da capicom
oStoreMem := win_oleCreateObject( 'CAPICOM.Store' )
// Aloca o certificado na memoria
BEGIN SEQUENCE WITH __BreakBlock()
oStoreMem:open( _CAPICOM_MEMORY_STORE, 'Memoria', _CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED )
RECOVER // CATCH oError
aRETORNO[ 'MSG' ] := 'Falha ao alocar o certificado na memoria ' + hb_eol() + ;
'Error: ' + Transform( oError:GenCode, nil ) + ';' + hb_eol() + ;
'SubC: ' + Transform( oError:SubCode, nil ) + ';' + hb_eol() + ;
'OSCode: ' + Transform( oError:OsCode, nil ) + ';' + hb_eol() + ;
'SubSystem: ' + Transform( oError:SubSystem, nil ) + ';' + hb_eol() + ;
'Mensangem: ' + oError:Description
RETURN aRETORNO
ENDSEQUENCE
// Aloca o certificado na Capicom
BEGIN SEQUENCE WITH __BreakBlock()
oStoreMem:Add( oCert )
RECOVER // CATCH oError
aRETORNO[ 'MSG' ] := 'Falha ao aloca o certificado na memoria da Capicom ' + hb_eol() + ;
'Error: ' + Transform( oError:GenCode, nil ) + ';' + hb_eol() + ;
'SubC: ' + Transform( oError:SubCode, nil ) + ';' + hb_eol() + ;
'OSCode: ' + Transform( oError:OsCode, nil ) + ';' + hb_eol() + ;
'SubSystem: ' + Transform( oError:SubSystem, nil ) + ';' + hb_eol() + ;
'Mensangem: ' + oError:Description
RETURN aRETORNO
ENDSEQUENCE
oXmldsig:store := oStoreMem
// Cria chave CSP
BEGIN SEQUENCE WITH __BreakBlock()
dsigKey := oXmldsig:createKeyFromCSP( oCert:PrivateKey:ProviderType, oCert:PrivateKey:ProviderName, oCert:PrivateKey:ContainerName, 0 )
RECOVER
aRETORNO[ 'MSG' ] := 'Erro ao criar a chave do CSP, talvez o certificado não esteja instalado corretamente.'
RETURN aRETORNO
ENDSEQUENCE
IF ( dsigKey = nil )
aRETORNO[ 'MSG' ] := 'Erro ao criar a chave do CSP.'
RETURN aRETORNO
ENDIF
// Assina a chave do CSP
BEGIN SEQUENCE WITH __BreakBlock()
signedKey := oXmldsig:Sign( dsigKey, 2 )
RECOVER
aRETORNO[ 'MSG' ] := 'Erro ao assinar a chave do CSP, talvez o certificado não esteja instalado corretamente.'
RETURN aRETORNO
ENDSEQUENCE
IF signedKey = NIL
aRETORNO[ 'MSG' ] := 'Assinatura Falhou.'
RETURN( aRetorno )
ENDIF
// Trata o formato da estrutura do XML
cXMLSig := StrTran( StrTran( oDOMDoc:xml, Chr( 10 ) ), Chr( 13 ) )
PosIni := At( '<SignatureValue>', cXMLSig ) + Len( '<SignatureValue>' )
cXMLSig := SUBS( cXMLSig, 1, PosIni - 1 ) + StrTran( SUBS( cXMLSig, PosIni, Len( cXMLSig ) ), ' ', '' )
PosIni := At( '<X509Certificate>', cXMLSig ) - 1
nP := At( '<X509Certificate>', cXMLSig )
nResult := 0
DO WHILE nP <> 0
nResult := nP
nP = WAT( '<X509Certificate>', cXMLSig, nP + 1 )
ENDDO
PosFim := nResult
cXMLSig := SUBS( cXMLSig, 1, PosIni ) + SUBS( cXMLSig, PosFim, Len( cXMLSig ) )
cXMLsig := StrTran( cXMLsig, 'Id="rps:1"', '' )
// grava o arquivo no disco
nHandle := FCreate( ::Xml )
FWrite( nHandle, cXMLSig )
FClose( nHandle )
aRETORNO[ 'STATUS' ] := .T.
aRETORNO[ 'XML' ] := ::Xml
aRETORNO[ 'MSG' ] := 'XML assinado com sucesso em ' + ::Xml
RETURN aRETORNO
Dá pra descomplicar, apenas uma prévia do que está diferente:
METHOD AssinaXml() CLASS NFSE
IF ::Xml = NIL .OR. Empty( ::Xml )
aRETORNO[ 'MSG' ] := 'Arquivo XML com o registro do cabeçalho e RPS não informado.'
ENDIF
cXML := MemoRead( ::Xml )
FErase( ::Xml )
nHandle := FCreate( ::ohbNFe:pastaEnvRes + "\edunfs.xml" )
FWrite( nHandle, cXML )
FClose( nHandle )
cRetorno := AssinaXml( @cXml, cCertificado )
// grava o arquivo no disco
nHandle := FCreate( ::Xml )
FWrite( nHandle, cXMLSig )
FClose( nHandle )
Com isso, a classe nem usa mais CAPICOM.
Lógico, quem usa é a rotina de assinatura (igual na SefazClass)
Esta aqui também:
METHOD ctPegaCNCertificado() CLASS NFSE
LOCAL oStore, oCertificados
LOCAL cSubjectName := '', cCN := ''
LOCAL mI
BEGIN SEQUENCE WITH __BreakBlock()
oStore := win_oleCreateObject( "CAPICOM.Store" )
END SEQUENCE
IF oStore = Nil
RETURN ''
ENDIF
oStore:open( _CAPICOM_CURRENT_USER_STORE, 'My', _CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED )
oCertificados := oStore:Certificates()
FOR mI = 1 TO oCertificados:Count()
IF oCertificados:Item( mI ):SerialNumber = ::ohbNFe:cSerialCert
cSubjectName := oCertificados:Item( mI ):SubjectName
ENDIF
NEXT
cCN := ''
FOR mI := At( "CN=", cSubjectName ) + 3 TO Len( cSubjectName )
IF SUBS( cSubjectName, mI, 1 ) == ","
EXIT
ENDIF
cCN += SUBS( cSubjectName, mI, 1 )
NEXT
oCertificados := Nil
oStore := Nil
RETURN cCN
Vamos pensar....
Se é pra manter o serial do certificado, pra depois pegar o NOME, então é só trabalhar diretamente com o nome, e não com o serial...
(igual na SefazClass)
É chato dizer isso, mas....
Tudo que usa HBNFE como modelo fica ruim, esqueçam os fontes da HBNFE.
É muito trabalho pra pouca coisa.