16 de maio de 2012

Preparando o Certificado Digital para inclusão no XML com CAPICOM e Delphi

Há algum tempo, publiquei uma série de posts sobre a manipulação de XMLs e a criação de assinatura digital para eles. Como a plataforma .NET da Microsoft traz classes prontas para realizar o grosso das tarefas envolvidas, eu usei o C# para montar os exemplos contidos naqueles posts.

Mas muita gente não pode usar a solução com .Net, principalmente quem já têm aplicações desenvolvidas em outras linguagens. Algumas dessas pessoas entraram em contato pra saber como fazer esse mesmo procedimento usando a biblioteca de criptografia da Microsoft - o CAPICOM. Uma dúvida bastante frequente é relativa ao conteúdo a ser inserido na tag X509Certificate do XML assinado. O quadro abaixo mostra o trecho de um XML assinado de Nota Fiscal Eletrônica que inclui a tag em questão:
<?xml version="1.0" encoding="UTF-8" ?>
<enviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<idLote>71</idLote>
<NFe>
<infNFe Id="NFe3508059978" versao="2.00">
<cUF>35</cUF>
<cNF>518005127</cNF>
<natOp>Venda a vista</natOp>
<mod>55</mod>
<serie>1</serie>
<dEmi>2012-05-06</dEmi>
<tpAmb>2</tpAmb>
</infNFe>
<Signature>
<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="#NFe3508059978"
<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>4aXU7m8rl14ALy6X...=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Igq7eI/Fy3PyjjSW...=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIGxDCCBaygAwIBAgIIN6q4...=</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</NFe>
</enviNFe>

Como podemos ver, a tag X509Certificate é parte da estrutura que traz as informações sobre a chave usada pela assinatura e é incluída no fim do XML, junto com a área reservada para a assinatura em si. O valor armazenado na X509Certificate descreve o certificado usado na assinatura, codificado na Base64. Esse valor representa a chave do certificado e é parte imprescindível para a validação do documento XML, isso é, ele ajuda a garantir que o XML não foi adulterado depois de ter sido assinado.

E como calcular esse valor com CAPICOM? Após recuperar o certificado digital de acordo com as instruções do post Acessando o Certificate Store do Windows com CAPICOM, podemos conseguir o conteúdo desse certificado já na Base64 em um único comando:
var cert : TCertificate;
certBase64 : WideString;
begin
{ ... }
certBase64 := cert.Export(CAPICOM_ENCODE_BASE64);
{ ... }
A função export da interface ICertificate2 aceita como parâmetro o tipo de codificação que se deseja como resultado da exportaçao. Os valores permitidos para esse parâmetros são os definidos no tipo enumerado CAPICOM_ENCODING_TYPE. Ele pode assumir basicamente dois tipo de codificação : a Base64 ou a informação binária pura, sem codificação.

O código utilizado no exemplo - CAPICOM_ENCODE_BASE64 - faz a exportação de todas as informações necessárias para inclusão na tag X509Certificate do XML assinado. Há, no entanto, um detalhe no qual é preciso prestar atenção: o texto exportado está quebrado em diversas linhas.
MIIGNjCCBR6gAwIBAgIITDgm4uwI+60wDQYJKoZIhvcNAQEFBQAwTDELMAkGA1UE
BhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxKDAmBgNVBAMTH1NFUkFTQSBDZXJ0
aWZpY2Fkb3JhIERpZ2l0YWwgdjEwHhcNMTEwNzA3MTMxMDA0WhcNMTIwNzA2MTMx
MDA0WjCB7jELMAkGA1UEBhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxFDASBgNV
BAsTCyhFTSBCUkFOQ08pMRgwFgYDVQQLEw8wMDAwMDEwMDE5NjExNzIxFDASBgNV
BAsTCyhFTSBCUkFOQ08pMRQwEgYDVQQLEwsoRU0gQlJBTkNPKTEUMBIGA1UECxML
.
.
.
aXRhbC5jb20uYnIvc2VyYXNhY2R2MTANBgkqhkiG9w0BAQUFAAOCAQEAY8NBFO6e
kPTHirS8DHnlsx1kTJfmsHoUR1QgnDMNdChQjW7ctAn5nIpPYXdhf26om+Us80D/
5RA2iXHhXFT5eL7Crqq0DQ73zWbdn8e81dIDFqYjxIt4LBMy4CO0lJeiT9YHkmYB
XSuADpElqwXA0rzx7bBeIOzuVUGMoYQBduViByI813XIQ2i86Z9VgRLO5KuPUNPn
0RkZS2rSpX1/wLkef8ura++dZEYmbVWpu3g2yCLQJfCArYy/fzsIDwBKruSWnY/O
NK/1AgUaceVAcxxvVmuECwCBqtpfmHqAOXC/uEOXiKefF7L++j0Xc+Okew1SMkHJ
sNNJ+AcX4VfQrw==

Como o conteúdo da tag X509Certificate deve estar numa única linha, o texto retornado pelo export deve ser tratado, removendo-se os espaços em branco do fim e as quebras de linha:
certBase64 := Trim (ReplaceStr (certBase64, #13#10, ''));

O valor da variável certBase64 agora pode ser incluído sem problemas como conteúdo da tag X509Certificate em seu XML assinado.

3 de maio de 2012

Trabalhando com HTTP em Delphi

O protocolo HTTP - HyperText Transfer Protocol ou Protocolo de Transferência de Hipertexto) - é certamente o mais utilizado na internet. É com ele que as páginas da internete são transferidas para seu computador antes de serem exibidas no navegador. O termo "hipertexto" diz respeito ao fato de que os documentos obtidos através do protocolo podem conter links remetendo a outros documentos.

Numa primeira olhada, a utilidade do HTTP não parece muito diferente do FTP usado para fazer downloads. O código abaixo, por exemplo, obtém o documento padrão do site do Google Brasil, isto é, a página HTML que é exibida quando se digita o endereço do Google Brasil num navegador. No exemplo, estou usando o componente TIdHTTP da paleta do projeto Indy:
var lResponse : TStringStream;
begin
lResponse := TStringStream.Create('');

try
{ lResponse conterá a página HTML requisitada :}
idHttp1.Get('http://www.google.com.br', lResponse);
finally
lResponse.Free();
end;
Apesar de não ser necessário configurar explicitamente um servidor/porta e usuário/senha como no FTP, o resultado final é o mesmo: o download de um arquivo remoto. Na verdade, a infraestrutura da internete mapeia automaticamente o endereço do Google para um servidor/porta; nesse caso, o usuário e senha é dispensável pois se trata de um site público que permite acesso anônimo.

Se olharmos mais a fundo, no entanto, veremos que o HTTP é mais flexível porque permite adicionar parâmetros à requisição enviada ao servidor. Na prática, isso dá ao servidor a chance de montar dinamicamente a resposta mais adequada à solicitação feita. As APIs de serviços publicados na internete normalmente são baseados no HTTP e se valem dos parâmetros pra flexibilizar as requisições. É o caso, por exemplo, do Google Docs e de outros serviços do Google, que usam a API de dados da empresa. Em outro post, eu mostro como se comunicar com o Google Docs usando a API de dados para ler os documentos salvos nesse serviço.

Se você já trabalhou com HTML, deve ter notado que a sintaxe dos Forms inclui uma "action", um método de envio e os diversos campos que serão preenchidos pelo usuário da página, sendo admitido até mesmo a existência de campos invisíveis contendo informações pré alimentadas.

Para relembrar o significado dessas propriedades do Form HTML : A "action" é o endereço na internete para onde a requisição será submetida. O "método" diz repespeito a como e quais informações serão enviadas; os valores mais comuns são GET (as informações são formatadas e acrescentadas ao endereço da "action", que então é enviado ao servidor) ou POST (as informações são transportadas à parte e não é possível enxergá-las na barra de endereço do navegador).

O form de pesquisa do Google, por exemplo, admite que você selecione a língua na qual deseja obter as repostas e, claro, os termos a serem pesquisados. O exemplo abaixo submete ao Google uma pesquisa em português com o nome do blog:
var lURL : String;
lResponse : TStringStream;
begin
lResponse := TStringStream.Create('');

try
lURL := 'http://www.google.com.br/search?' +
'hl=pt-BR&' +
'q=balaio%20tecnologico';
idHttp1.Get(lURL, lResponse);
lResponse.Position := 0;
{ Exemplo de uso do response : carregar o conteúdo num RichEdit : }
reResp.Lines.LoadFromStream(lResponse);
finally
lResponse.Free();
end;
Veja que cada parâmetro é composto de um nome (o mesmo configurado nas tags input do form) seguido por um sinal de igual (=) e o valor que o parâmetro deve assumir (normalmente, os próprios valores fornecidos pelo usuário). Cada parâmetro é concatenado ao anterior através de um "E Comercial" (&).

Ambos os parâmetros considerados pelo exemplo estão incluídos no form da pesquisa do Google. Ou seja, se você souber quais são os parâmetros de um form, poderá simular via Delphi a requisição que ele submeteria numa página da Web e, então, obter exatamente a mesma reposta, podendo tratá-la do modo que for mais apropriado para sua aplicação.

Por causa dessa facilidade de simular requisições via programação, muitos sites se protegem implementando um CAPTCHA. Trata-se de uma imagem com um texto embutido que é gerada dinamicamente pelo servidor de modo que apenas operadores humanos consigam ler. O texto deve, então, ser digitado num campo e enviado junto com as demais informações. A requisição só é atendida se o texto estiver correto.

Como podemos notar pelo exemplo, usar o GET para recuperar um documento pode ser problemático se certas informações sensíveis - como uma senha - tiverem que ser transmitidas direto na URL. Nestes casos, é recomendado usar o POST pois com ele as informações trafegam por outros meios.

O uso do POST também é simples. Basta passar os parâmetros num TStringList separado. A URL do "action" é informada sem quaisquer decorações extras:
var lParams :TStringList;
lResponse : TStringStream;
begin
lParams := TStringList.Create;
lResponse := TStringStream.Create('');

try
lParams.Add('filt=all');
lParams.Add('p=balaio%20tecnologico');
idHttp1.Post('http://br.search.yahoo.com/search', lParams, lResponse);

{ Exemplo de uso do response : carregar o conteúdo num RichEdit : }
lResponse.Position := 0;
reResp.Lines.LoadFromStream(lResponse);
finally
lParams.Free();
lResponse.Free();
end;
Neste exemplo, usei o Yahoo! porque o Google não implementa POST para pesquisas.

Ajustes ainda mais finos para submeter a requisição podem ser configurados na propriedade Request do TIdHTTP. Com ele, é possível ajustar o tipo de documento que se espera receber de volta (HTML, XML, etc.), o tipo de encoding, se há necessidade de passar por um proxy, etc.