17 de julho de 2009

Verificando assinaturas digitais com CAPICOM

Continuando a série sobre Certificados e assinaturas digitais usando a biblioteca CAPICOM, falo agora sobre como verificar se uma assinatura é válida, isto é, se os dados que foram assinados não foram adulterados de alguma forma.

Vou usar aqui o mesmo exemplo do post sobre assinatura digital com CAPICOM. Naquele exemplo, assinei o conteúdo de um arquivo e salvei o resultado num outro arquivo. Ao assinar, programei o CAPICOM para incluir na assinatura todo o conteúdo assinado.

Neste caso, para verificar se a assinatura é válida, é preciso apenas instanciar a mesma classe que foi usada para assinar: a TSignedData. Diferentemente do processo de assinatura, a validação não exige configuração extra do TSignedData pois a chave pública necessária já foi incluída na própria assinatura. O código abaixo mostra a validação da assinatura gerada no post anterior:
var lSignedData: TSignedData;
fs : TFileStream;
qt : integer;
ch : PWideChar;
msg : WideString;
ok : boolean;
begin
fs := TFileStream.Create (edMsgFile.Text, fmOpenRead);
New (ch);
repeat
qt := fs.Read(ch^, 2);
if qt > 0 then
msg := msg + ch^;
until qt = 0;
fs.Free;
Dispose (ch);

lSignedData := TSignedData.Create(self);

try
ok := false;
lSignedData.Verify (msg, false, CAPICOM_VERIFY_SIGNATURE_ONLY);
msg := lSignedData.Content;
ok := true;
except
on exc: Exception do
ShowMessage (exc.Message);
end;

lSignedData.Free;

if ok then
ShowMessage ('Conteúdo validado com sucesso !');
end;

Na primeira parte do código, leio o arquivo com a assinatura e jogo esse conteúdo para a variável msg. Lembre-se de que a assinatura foi gerada num WideString e que, portanto, cada caractere é representado por 2 bytes. Por isso, a leitura é feita usando um WideChar e lendo 2 bytes por vez antes do valor ser concatenado ao WideString com a assinatura.

Depois, crio a instância do TSignedData e apenas chamo sua função Verify para realizar a validação. O primeiro parâmetro desta função é o conteúdo assinado (que foi lido do arquivo); o segundo parâmetro tem o nome de Detached, significando "solto" ou "desprendido" em referência ao fato de indicar se o conteúdo original está "desprendido" ou se foi embutido na assinatura - por isso passo false nele: o conteúdo não está desprendido já que fiz a assinatura pedindo para inserir o conteúdo. O último parâmetro é um indicador do quê será validado: somente a assinatura ou validar também o certificado.

Se tudo ocorreu bem na verificação, a propriedade Content da classe TSignedData agora possui o mesmo conteúdo que foi originalmente assinado, isto é, este processo também pode ser usado para criptografar os dados e de quebra saber se eles não foram adulterados já que para assiná-lo é preciso ter a chave privada e só você deveria ter esta informação.

No caso de um erro acontecer na validação, uma exceção é levantada. Então, incluí o processo de validação num bloco try-except para poder tratar a mensagem retornada.

E se a assinatura for gerada sem o conteúdo ? Como validar ? Simples: alimente manualmente a propriedade Content antes de chamar o Verify. Nessa situação, tanto o conteúdo original quanto a assinatura têm que estar presentes para a validação funcionar.

12 comentários :

Herson disse...

Otimo artigo! Mas você poderia postar tambem os fontes em delphi, com a implementação deste recurso. Fica totalmente completa sua informação.

Luís Gustavo Fabbro disse...

Herson

O código necessário está praticamente todo no blog. A parte que não está neste post pode ser encontrada nos outros 2 indicados em "Mais Informações", no fim do texto.

Só não está publicada a unit resultante da importação do COM pelo Delphi porque é melhor ter uma versão dela gerada pelo seu próprio IDE - pode haver diferenças importantes na geração em versões diferentes do Delphi.

Há um roteiro simples
neste post mostrando como fazer a importação e gerar a unit.

Unknown disse...

Como faço para pegar a data do arquivo da certificação, ou seja, pegar a data que foi assinado digitalmente o arquivo ?

Luís Gustavo Fabbro disse...

Ricardo

Até onde eu sei, a data da assinatura não é registrada automaticamente. Você terá que controlar isso "na mão" - talvez incluindo a informação em algum ponto do documento antes de assiná-lo.

[]s

Anônimo disse...

Luís
Boa tarde...
Preciso ler um arquivo qualquer(.rtf ou .pdf por exemplo), gerar um hash usando o algoritmo SHA256, assinar digitalmente(SHA256), gerando um arquivo no formado dsig ou pkcs#7... eu vi seus artigos, mas não consegui fazer funcionar em delphi2006... vc teria um fonte completo? ou mais dicas?

Luís Gustavo Fabbro disse...

A classe HashedData do CAPICOM permite o cálculo de hashes, inclusive com o algorítimo SHA256.

De resto, o que vc não conseguiu fazer ? Parece que o resultado do processo de assinatura com CAPICOM é um pkcs#7 que inclui a assinatura, as chaves públicas dos certificados envolvidos e, opcionalmente, o próprio conteúdo assinado.

[]s

Unknown disse...

Luís
Bom dia....
Como faço para extrair (pegar) apenas o conteúdo original, ou seja, sem os caracteres da assinatura do arquivo PKCS#7.

Luís Gustavo Fabbro disse...

Márcio

Mesmo passando FALSE no parâmetro detached do Verifiy vc não conseguiu extrair o valor originalmente criptografado? Veja se não há algum problema na rotina que faz a criptografia; ela deve garantir a inclusão do valor original.

[]s

Unknown disse...

Luís,
Consegui obter as informações na var msg , que está aparentemente criptografada, porém como faço para salvar apenas os dados, no caso o texto original, pois após a validação da assinatura, preciso repassar apenas a mensagem original (antes da assinatura) para outra equipe.

Luís Gustavo Fabbro disse...

No código listado no post, essa recuperação é feita pela linha:
msg := lSignedData.Content;

Veja que a variável msg é um WideString, ou seja, cada caractere ocupa 2 bytes. Faça a conversão para AnsiString se seu conteúdo não precisa desse recurso.

[]s

Unknown disse...

Consegui assinar no formato PKCS#7, como extrair esse conteúdo, ou seja nome,cpf,data da assinatura, autoridade certificadora, etc . Esse seu exemplo faz isso?

Luís Gustavo Fabbro disse...

Yrece

Ao verificar a mensagem, o SignedData popula a propriedade Certificates com informações do certificado usado na assinatura, bem como a cadeia validadora dele (se houver). Navegue pelas propriedades dos certificados para localizar as informações desejadas.

[]s

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.