10 de junho de 2013

Problema com Hash e assinatura digital usando CAPICOM e Delphi

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!

10 de janeiro de 2013

SpeechMagic e SpeechMike

Na empresa onde trabalho, acabei de concluir a integração entre nosso software, que é construído com o Delphi e o software de reconhecimento de voz da Nuance: "SpeechMagic". Alem disso, também integrei o microfone da Philips: "SpeechMike". O editor usado na integração é o TRichEdit.

Quem representa e revende o "SpeechMagic" e o "SpeechMike" aqui no Brasil é a Macsym (www.macsym.com.br).

Quem tiver interesse, pode entrar em contato :)

Abraço a todos!