Faz um tempo que necessitei trabalhar com calculo de "hash" e também com "assinatura digital". Para isso, tentei usar o "CAPICOM SDK" da Microsoft mas me deparei com problemas que até hoje eu não tinha encontrado uma solução.
1 - O primeiro problema que encontrei foi com o método "Hash" da classe "HashedData". Percebi que o calculo retornado não estava correto. Depois de pesquisar sobre o caso identifiquei que alterando o tipo do parâmetro do método "Hash" de "WideString" para "String", o problema foi resolvido.
Vejam como o Delphi mapeia o tipo do parâmetro quando fizemos a importação da capicom.dll:
----------------------------------------------
IHashedData = interface(IDispatch)
['{9F7F23E8-06F4-42E8-B965-5CBD044BF27F}']
function Get_Value: WideString; safecall;
function Get_Algorithm: CAPICOM_HASH_ALGORITHM; safecall;
procedure Set_Algorithm(pVal: CAPICOM_HASH_ALGORITHM); safecall;
procedure Hash(const newVal: WideString); safecall;
property Value: WideString read Get_Value;
property Algorithm: CAPICOM_HASH_ALGORITHM read Get_Algorithm write Set_Algorithm;
end;
----------------------------------------------
Agora, vejam a alteração que fiz na "CAPICOM_TLB.pas" para funcionar corretamente:
----------------------------------------------
IHashedData = interface(IDispatch)
['{9F7F23E8-06F4-42E8-B965-5CBD044BF27F}']
function Get_Value: WideString; safecall;
function Get_Algorithm: CAPICOM_HASH_ALGORITHM; safecall;
procedure Set_Algorithm(pVal: CAPICOM_HASH_ALGORITHM); safecall;
procedure Hash(const newVal: String); safecall;
property Value: WideString read Get_Value;
property Algorithm: CAPICOM_HASH_ALGORITHM read Get_Algorithm write Set_Algorithm;
end;
----------------------------------------------
2 - O segundo problema que encontrei foi com o método "Sign" da classe "SignedData". Percebi que a assinatura retornada não estava correta. Depois de pesquisar ainda muito mais que no primeiro caso identifiquei que alterando o tipo da propriedade "Content" de "WideString" para "String" e alterar a implementação do método "Set_Content", o problema foi resolvido.
Vejam como o Delphi mapeia o tipo da propriedade quando fizemos a importação da capicom.dll:
----------------------------------------------
ISignedData = interface(IDispatch)
['{AE9C454B-FC65-4C10-B130-CD9B45BA948B}']
procedure Set_Content(const pVal: WideString); safecall;
function Get_Content: WideString; safecall;
function Get_Signers: ISigners; safecall;
function Get_Certificates: ICertificates; safecall;
function Sign(const pSigner: ISigner; bDetached: WordBool; EncodingType: CAPICOM_ENCODING_TYPE): WideString; safecall;
function CoSign(const pSigner: ISigner; EncodingType: CAPICOM_ENCODING_TYPE): WideString; safecall;
procedure Verify(const SignedMessage: WideString; bDetached: WordBool;
VerifyFlag: CAPICOM_SIGNED_DATA_VERIFY_FLAG); safecall;
property Content: WideString read Get_Content write Set_Content;
property Signers: ISigners read Get_Signers;
property Certificates: ICertificates read Get_Certificates;
end;
----------------------------------------------
Agora, vejam a alteração que fiz na "CAPICOM_TLB.pas" para funcionar corretamente:
----------------------------------------------
ISignedData = interface(IDispatch)
['{AE9C454B-FC65-4C10-B130-CD9B45BA948B}']
procedure Set_Content(const pVal: String); safecall;
function Get_Content: String; safecall;
function Get_Signers: ISigners; safecall;
function Get_Certificates: ICertificates; safecall;
function Sign(const pSigner: ISigner; bDetached: WordBool; EncodingType: CAPICOM_ENCODING_TYPE): WideString; safecall;
function CoSign(const pSigner: ISigner; EncodingType: CAPICOM_ENCODING_TYPE): WideString; safecall;
procedure Verify(const SignedMessage: WideString; bDetached: WordBool;
VerifyFlag: CAPICOM_SIGNED_DATA_VERIFY_FLAG); safecall;
property Content: String read Get_Content write Set_Content;
property Signers: ISigners read Get_Signers;
property Certificates: ICertificates read Get_Certificates;
end;
----------------------------------------------
Vejam como o Delphi implementa o método "Set_Content" quando fizemos a importação da capicom.dll:
----------------------------------------------
procedure TSignedData.Set_Content(const pVal: WideString);
{ Warning: The property Content has a setter and a getter whose
types do not match. Delphi was unable to generate a property of
this sort and so is using a Variant as a passthrough. }
var
InterfaceVariant: OleVariant;
begin
InterfaceVariant := DefaultInterface;
InterfaceVariant.Content := pVal;
end;
----------------------------------------------
Agora, vejam a alteração que fiz na "CAPICOM_TLB.pas" para funcionar corretamente:
----------------------------------------------
procedure TSignedData.Set_Content(const pVal: WideString);
{ Warning: The property Content has a setter and a getter whose
types do not match. Delphi was unable to generate a property of
this sort and so is using a Variant as a passthrough. }
var
InterfaceVariant: ISignedData;
begin
InterfaceVariant := DefaultInterface;
InterfaceVariant.Content := pVal;
end;
----------------------------------------------
Em relação ao problema 1, tenho certeza de ter corrigido pois pude comparar o calculo do hash com outras ferramentas.
Em relação ao problema 2, o único parâmetro que tive até agora para comparar e decretar que o problema foi resolvido, foi fazer a verificação da assinatura gerada pelo CAPICOM em outro software, o Bry Signer (http://www.bry.com.br/). Como o Bry Signer já é utilizado em produção e vendido no mercado, é certo que a assinatura digital realizada por ele funciona corretamente, portanto, devido a uma assinatura digital gerada pela CAPICOM ser verificada e validada com sucesso através dele, concluí que o problema foi resolvido.
Quem passou pelo mesmo problema e aplicou uma solução diferente, por favor, deixe seu comentário.
Abraço!
Estou interessado em um aplicativo que usa PKCS#7 para assinar documento em PDF P7s e outro para ler esse documento e verificar a autencidade dele como o BRY signer
ResponderExcluiremail para contato ytrench@gmail.com
Olá, acabei de lhe enviar um e-mail.
ResponderExcluirAlexandre, tenho algumas dúvidas relacionadas a esse assunto, poderia, por favor, me ajudar?
ResponderExcluirMeu e-mail é geazi.gp@gmail.com
Att.,
Geazi
Alterei conforme sugerido e o erro continua. Alguma sugestão? diego.alamini@gmail.com
ResponderExcluiroi Diego, acabei de lhe enviar um e-mail.
ExcluirOlá Alexandre, estou com dificuldade de assinar documentos com Delphi (já faço com XML, mas agora preciso assinar documentos simples, como txt, por exemplo). Tem algo que posso me ajudar? saviocler@gmail.com
ResponderExcluirAgradeço.
oi Sávio, acabei de lhe enviar um e-mail.
ExcluirEste comentário foi removido pelo autor.
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirOla Alexandre, fiz as alterações que você falou e continua dando erro. Quando vou assinar, da mensagem classe não registrada. Pode me ajudar? Isso acontece com arquivo PDF. Deste já, muito obrigado.
ResponderExcluirMeu e-mail é jorge@webbrasilia.com
Consegui resolver. O capicom não tava registrado. Agora tá dando erro no novo PDF, ele não abre. Obrigado.
ExcluirOla Alexandre, pode me ajudar. O PDF gerado não abre. Grato.
ResponderExcluir