| Por Gilberto Uchôa gilbertouchoa@yahoo.com.br Gilberto Uchôa trabalha na Hepta Informática desde 1993, sempre na área de desenvolvimento, atuando em projetos como gerente de projeto e arquiteto de soluções, e atuando nas áreas de consultoria e treinamento, ministrando os cursos oficiais das várias plataformas de desenvolvimento da Microsoft. |
|
|
|
|
| Assinaturas Digitais Usando WSE | |
|
|
|
Assinatura Digital
Uma
informação de alguma forma associada a uma mensagem, identificando de forma
inequívoca a origem desta mensagem.
WSE
Web Services Enhancements, um SDK da Microsoft que implementa os padrões "WS-*" da OASIS (Organization for the
Advancement of Structured Information Standards), um grupo similar em
funcionamento ao W3C, que "recomenda" padrões a serem usados nas
diversas áreas da indústria de software. O padrão "WS-*"
é na verdade uma série de recomendações em várias áreas relativas a web
services, tais como WS-Security (segurança de web services), WS-Routing
(roteamento end-to-end de mensagens usando web services),
WS-Attachments (anexação de documentos em mensagens SOAP), e outros.
Este artigo apresenta a o
conceito de assinatura digital, a importância de uso das assinaturas digitais
para garantir a identidade do originador de uma mensagem e um exemplo de
implementação de geração e verificação de assinaturas digitais usando a
biblioteca WSE - Web Services Enhancements da Microsoft.
Não-Repudiação: A Necessidade de
Garantir a Origem de Uma Mensagem
Assinaturas e Autenticação
Assinaturas Digitais
Os Detalhes de Implementação
Usando WSE para Gerar Uma
Assinatura Digital - Preparação
Obtendo um Certificado para Testes
Exportando o Certificado para
Arquivo
Instalando
o WSE
Usando WSE para Gerar Uma
Assinatura Digital - Codificação
Geração da Assinatura
Verificação da Assinatura
Pontos Finais
Repudição (repudiation) é uma forma
de ataque a um sistema informatizado. Basicamente consiste em o usuário dizer
"Não fui eu!". A "versão web service" deste ataque é bem
simples: você expõe um serviço da sua empresa via web service. O seu cliente
chama o web service, fazendo com que a sua empresa realize um trabalho em
benefício dele, e na hora de apresentar a fatura, você ouve "Mas não fui
eu!".
Veja que isto só pode ser
evitado se você tiver uma forma inequívoca de comprovar que sim,
realmente foi ele quem executou a chamada. Estes mecanismos são chamados de mecanismos
de não-repudiação (non-repudiation), e são
implementados com o uso de alguma forma de assinatura digital.
A assinatura digital
funciona exatamente como a assinatura que você coloca em um documento. Suponha
que você vai vender o seu apartamento. Você faz o seu "instrumento
particular de compra e venda", que é o contrato de venda do apê. O
comprador revisa este contrato e diz "ok, concordo com tudo". Mas a
maneira de formalizar a mensagem "ok, concordo com o contrato" é
assiná-lo. A assinatura garante que ele está de acordo com o contrato, e serve
para evitar que ele diga depois "Mas eu não concordei com o
contrato!!!". Agora, ele ainda pode alegar que a assinatura não é
a dele. Para isto, é envolvida no negócio uma terceira entidade, o
cartório. O cartório é uma parte isenta no negócio, e as outras duas partes
envolvidas - você e seu comprador - implicitamente confiam nele. Então
desta forma a autenticidade de uma assinatura pode ser estabelecida:
Desta forma, caso o
comprador mais tarde diga "Mas eu não concordei com o contrato!!!",
você retira o contrato assinado por ele, e com a assinatura
reconhecida pelo cartório, e esfrega na fuça dele. Veja que somente a
assinatura não basta; deve haver a autenticação da assinatura para que o
documento tenha validade legal.
A assinatura digital
funciona exatamente da mesma forma. Suponha que você tem um serviço de entrega
de pacotes. Você criou um site, e criou um web service para que seus clientes
possam solicitar a entrega de um pacote:
<WebMethod()> _
Public
Sub NovaEntrega( _
ByVal
origem As Endereco, _
ByVal
destino As Endereco, _
ByVal
idPacote As String,
_
ByVal
dataHoraRecebimento As DateTime)
Outras empresas podem
chamar o seu web service. Seu "Gerente Dos Motoboys" tira o relatório
com as entregas a fazer no dia e despacha o pessoal. No fim do mês, sua empresa
envia a nota de cobrança para todo mundo que usou seus serviços, cobrando todas
as entregas que foram realizadas no mês.
A questão óbvia é: como
evitar que, quando da apresentação da nota, o seu cliente fale "Mas não
fui eu quem fez esse pedido!"? Para isto você vai usar assinaturas
digitais. De uma forma geral, o processo de uso da assinatura digital é o
seguinte:
Veja agora que fica simples
evitar o problema de repudição do nosso serviço de entregas. Você coloca como
exigência básica de uso do seu serviço que a empresa que está usando o serviço
tem que ter um certificado digital. O sistema que está chamando o seu web
service gera uma assinatura do pedido sendo feito e passa esta assinatura como
parâmetro do método:
<WebMethod()> _
Public
Sub NovaEntrega( _
ByVal
origem As Endereco, _
ByVal
destino As Endereco, _
ByVal
idPacote As String,
_
ByVal
dataHoraRecebimento As DateTime, _
ByVal assinaturaPedido
As Byte())
Seu web service recebe o
pedido, valida a assinatura, assim garantindo sua origem, e armazena a
assinatura em caso de necessidade de validação de um determinado pedido.
"Tá, eu entendi como
eu uso a assinatura digital. Mas o quê tem dentro dela? Como ela
garante que eu sou eu?"
A assinatura digital se
baseia no uso de chaves públicas e privadas de criptografia. Estas chaves são
sempre geradas aos pares, sendo matematicamente relacionadas.Uma informação
criptografada com a chave pública só pode ser descriptografada com a chave
privada, e vice-versa. É no "vice-versa" que estamos interessados.
A assinatura digital é
gerada da seguinte forma:
O resultado da criptografia do hash da mensagem original é a assinatura digital da mensagem. Para verificar a mensagem, deve ser feito o seguinte:
"Aaaahhnnntááááá. Mas
e o certificado digital? Aonde ele entra nessa parada?"
O certificado digital
garante a identidade do dono da chave pública. Lembre que ele é um documento,
contendo a chave pública em questão, e assinado pela CA, que garante que
verificou a identidade do pretenso dono daquela chave pública. Então ao
verificar uma assinatura usando uma chave pública que está dentro de um
certificado digital, você tem certeza de quem criou a assinatura,
desta forma identificando inequivocamente o originador da mensagem.
O WSE facilita a geração e
conferência de assinaturas digitais ao disponibilizar uma classe que consegue
carregar um certificado digital, e extrair dele as chaves pública e/ou privada
presentes no certificado. De posse destas chaves, fica fácil gerar ou verificar assinaturas,
usando as classes do namespace System.Security.Cryptography.
A biblioteca de classes do
.NET Framework implementa os dois algoritmos de assinatura digital mais comuns
hoje em dia: RSA (Rivest, Shamir e Adleman - seus inventores) e DSA (Digital
Signature Algorithm). Vamos usar o RSA, pela simples razão de que foi o
algoritmo usado para criar o certificado digital que usei nos testes.
A primeira providência que
você deve fazer é obter um certificado digital para testes. Se sua empresa tem
um, ótimo. Se você pode obter este certificado para fins de testes, coitada da
sua empresa. O certificado pode ser usado para assinar documentos, então ele
deveria ser guardado dentro da sala do presidente, trancado dentro da gaveta
dele. Você pode gerar um certificado de testes (veja esse outro post do meu blog), ou você pode obter um de graça na GlobalSign. Recomendo que você use este último
para obter um certificado de teste para este artigo - a geração do certificado
de testes requer alguns passos a mais do que o que está descrito aqui.
Qualquer que seja o método
que você usar, após a obtenção do certificado, ele estará instalado na sua
máquina. Você pode acessá-lo através do snap-in Certificates
do MMC:
1.
Clique
em Start | Run, digite mmc.exe e
pressione Ok. Uma console vazia do MMC será aberta.
2. Selecione a opção de menu File | Add/Remove Snap-in..., clique em Add..., e selecione Certificates. Clique em Add, deixe o default My user account, e clique em Finish. Feche todas as janelas (exceto a do MMC ;).
3.
Abra o
caminho "Certificates - Current
User | Personal | Certificates". Seu certificado de testes deve estar
lá dentro.

Para nos aproximarmos mais
de uma situação real, vamos gravar o certificado obtido em arquivo. Você pode
gravar o certificado de duas formas: com a chave privada ou sem a chave
privada.
§
Você
pode exportar o certificado com a chave privada para, p.e., mantê-lo em uma
mídia removível (memória flash, pen drive, smart card) e só fornecê-lo quando
necessário. Isto aumenta bastante a segurança da aplicação.
§
Você pode
exportar o certificado sem a chave privada para, p.e., disponibilizá-lo para download para qualquer
um que deseja verificar assinaturas digitais geradas por você. Isto poderia se
aplicar à nossa situação.
Ainda no snap-in Certificates, que usamos no passo anterior, faça o seguinte:
1.
Clique
com o botão direito no certificado a exportar.
2.
Selecione
a opção All Tasks | Export....
3.
Na
tela de apresentação do Wizard de Exportação de Certificado, clique Next.
4.
Na
tela Export Private Key, deixe
selecionado o valor default No, do not
export the private key.
5.
Na
tela Export File Format, deixe
selecionado o valor default DER encoded
binary X.509 (.CER). Este é o único formato de arquivo de certificado
reconhecido pela biblioteca WSE.
6.
Na
tela File to Export, selecione o
caminho e nome do arquivo. Vamos chamá-lo de CertificadoChavePublica.cer neste artigo.
7.
Na
tela final, clique Finish.
Agora temos o arquivo CertificadoChavePublica.cer, que contém
só a chave pública, e pode ser fornecido para qualquer pessoa que tenha que
verificar a assinatura gerada.
O WSE pode ser baixado de http://msdn.microsoft.com/webservices/building/wse/,
ou da página de downloads da Microsoft, caso eles retirem este link. A versão
mais atual quando da elaboração deste artigo era a 2.0 sp3, mas o exemplo que
eu fiz foi produzido na versão 1.0 sp1. Como houveram muitas modificações de
uma versão para outra, recomendo que só para efeito de estudo você instale a
versão 1.0 sp1 para acompanhar este artigo.
Tá bom, geek. Abra o VS.NET
e crie uma nova aplicação Windows Form. E como infelizmente aqui no meu
trabalho só tem VB.NET, e eu estou fazendo este artigo em uma ensolarada tarde
de trabalho, pois hoje é véspera das minhas férias e ainda por cima o chefe não
veio, vai ser VB.NET mesmo.
A nossa interface vai ser a
seguinte:

Primeiro de tudo,
acrescente uma referência para o WSE (Microsoft.Web.Services.dll),
e já coloque os seguintes Imports:
Imports
Microsoft.Web.Services.Security.X509
Imports System.Text
Imports
System.Security.Cryptography
A título de simplicidade,
vamos supor que a nossa mensagem é uma string, e vamos gerar sua assinatura.
Veja que este esquema se adequa bem a qualquer situação, pois qualquer que seja
a sua mensagem, você pode facilmente criar uma representação dela como string,
escrevendo-a como XML, e usar o que vamos ver aqui.
O click do nosso botão Gera Assinatura é bem simples:
Private Sub
LinkLabel1_LinkClicked( _
ByVal
sender As Object,
ByVal e As
LinkLabelLinkClickedEventArgs) _
Handles LinkLabel1.LinkClicked
Dim assinatura As Byte() = GeraAssinatura(TextBox1.Text)
TextBox2.Text = Convert.ToBase64String(assinatura)
End Sub
A função GeraAssinatura gera um array de bytes a
partir da string a assinar, e invoca a função que realmente faz a assinatura
digital:
Function GeraAssinatura(ByVal
mensagem As String)
As Byte()
Dim
mensagemEmBytes As Byte()
= Encoding.UTF8.GetBytes(mensagem)
Dim assinatura As Byte() = Assina(mensagemEmBytes)
Return assinatura
End Function
A função Assina realiza o algoritmo que citamos
antes: geração do hash da mensagem a assinar (pelo método ComputeHash da classe HashAlgorithm,
usando o algoritmo SHA-1), depois
gera a assinatura usando o hash gerado. A classe X509Certificate pertence ao WSE, e é usada para carregar o
certificado, mediante sua thumbprint
(veja a próxima função para um detalhamento disto). Veja que a propriedade Key do certificado retorna um objeto da
classe System.Security.Cryptography.RSA,
que é a chave privada contida no certificado. Usamos então Key.ExportParameters(True) para criar um objeto RSACriptoServiceProvider, que
representa a chave privada, e disponibiliza o método de geração de assinatura
digital (SignHash). E a assinatura é
gerada.
ReadOnly HASH_CERTIFICADO As
Byte() = _
{&H5, &HB7, &HD9, &H34, &H6B, &HDA, &H5A,
&H75, &H64, &HA2, _
&HC4, &H68, &HC3,
&HC1, &HD9, &H4A, &HDE, &HB5, &H62, &H18}
Function Assina(ByVal
dados As Byte())
As Byte()
' Carrega o certificado com a chave privada que será usada
para assinar a mensagem
Dim certificado As X509Certificate =
CarregaCertificado(HASH_CERTIFICADO)
' Gera o hash da informação a enviar
Dim algoritmoHash As
HashAlgorithm =
HashAlgorithm.Create("SHA1")
Dim hash As Byte() = algoritmoHash.ComputeHash(dados)
' Carrega um objeto RSA com a chave privada presente no
certificado
Dim algoritmoAssinatura As
New RSACryptoServiceProvider
algoritmoAssinatura.ImportParameters(certificado.Key.ExportParameters(True))
' Gera a assinatura
Dim assinatura As Byte() = algoritmoAssinatura.SignHash(hash, _
CryptoConfig.MapNameToOID("SHA1"))
Return assinatura
End Function
Finalmente, o método de
carga do certificado carrega o certificado do certificate store (o repositório de certificados), que são arquivos
e/ou entradas de registry feitas para o armazenamento de certificados digitais.
É verificado se o certificado foi criado para assinaturas digitais (você
solicita isto quando da criação do certificado), e se ele contém uma chave
privada (sem a qual não podemos gerar a assinatura digital).
Function CarregaCertificado(ByVal
hashCertificado As Byte())
As X509Certificate
Dim repositorioCertificados As
X509CertificateStore
Try
' Abre o repositório de certificados do usuário corrente
repositorioCertificados = _
X509CertificateStore.CurrentUserStore(X509CertificateStore.MyStore)
If Not
repositorioCertificados.OpenRead() Then
Throw New
Exception("Não foi possível abrir o certificate store.")
End If
' Localiza o certificado a usar na assinatura pelo seu hash
Dim certificados As
X509CertificateCollection = _
repositorioCertificados.FindCertificateByHash(hashCertificado)
Select Case
certificados.Count
Case
0 : Throw New
Exception("Certificado não encontrado.")
Case 1 ' Tudo ok, achou o
certificado
Case Is
> 1 : Throw New
Exception("Mais de um certificado foi " + _
"encontrado para o hash
fornecido.")
Case Else : Throw New Exception("X509CertificateCollection <
0")
End Select
Dim certificado As
X509Certificate = certificados(0)
' Verifica se o certificado pode ser usado para assinaturas
digitais
Select Case True
Case
Not certificado.SupportsDigitalSignature
Throw New
Exception("Certificado não suporta assinaturas digitais.")
Case certificado.Key Is Nothing
Throw New
Exception("Certificado não possui chave privada - " + _
"não pode ser usado para gerar uma
assinatura digital.")
End Select
Return
certificado
Finally
If
Not repositorioCertificados Is Nothing Then repositorioCertificados.Close()
End Try
End Function
Veja que o certificado é
carregado mediante seu thumbprint.
Isto nada mais é do que um hash do certificado que é usado para que um programa
possa solicitar a carga de um certificado específico do repositório de
certificados. O thumbprint do
certificado pode ser obtido ao se dar um duplo-clique no certificado no snap-in Certificates do MMC:

Copie e cole o valor da
caixa de texto no seu fonte, e acrescente "&H" antes de cada par
de dígitos. Veja o array HASH_CERTIFICADO
na função Assina apresentada
anteriormente.
Ok, agora você já pode
testar a geração da assinatura. Rode o programa, forneça um valor na caixa de
texto Mensagem, e pressione Gera Assinatura. O valor da assinatura
deverá aparecer na caixa de texto Assinatura.
Para verificar a
assinatura, vamos supor que estamos em outra aplicação, rodando em outra
máquina. Então vamos usar a chave pública gravada no arquivo de exportação que
geramos antes para verificar a assinatura.
O botão Verifica Assinatura da nossa aplicação
também é bem simples. Convertemos a assinatura (que está como uma string
base-64 na segunda caixa de texto) para bytes e chamamos o método de verificar
assinaturas, informando a assinatura, a mensagem a qual ela (supostamente) se
refere, e o arquivo aonde está a chave pública que deve ser usada para a
conferência da assinatura:
Private
Sub LinkLabel2_LinkClicked( _
ByVal sender As
Object, ByVal e
As LinkLabelLinkClickedEventArgs) _
Handles LinkLabel2.LinkClicked
Dim assinatura As
Byte() =
Convert.FromBase64String(TextBox2.Text)
If VerificaAssinatura(assinatura, TextBox1.Text, _
"C:\Temp\CertificadoChavePublica.cer")
_
Then
MsgBox("Assinatura
confere!", MsgBoxStyle.Information)
Else
MsgBox("Assinatura NÃO
confere.", MsgBoxStyle.Critical)
End If
End Sub
O método VerificaAssinatura
executa o algoritmo de verificação de assinaturas que vimos antes: ele calcula
o hash da mensagem com o mesmo algoritmo que o criador da assinatura deve ter
usado (SHA1 no nosso caso), carrega a chave pública e solicita à classe RSACryptoServiceProvider para verificar
a assinatura digital (método VerifyHash).
Function VerificaAssinatura( _
ByVal assinatura As Byte(), _
ByVal mensagem As String, _
ByVal nomeArquivoCertificado As
String _
) As Boolean
' Calcula o hash da informacao recebida
Dim mensagemEmBytes As
Byte() = Encoding.UTF8.GetBytes(mensagem)
Dim algoritmoHash As
HashAlgorithm = HashAlgorithm.Create("SHA1")
Dim hash As Byte() = algoritmoHash.ComputeHash(mensagemEmBytes)
' Carrega o certificado com a chave publica do cliente
Dim certificado As
X509Certificate = CarregaCertificado(nomeArquivoCertificado)
' Verifica a assinatura recebida
Dim algoritmoAssinatura As
New RSACryptoServiceProvider
algoritmoAssinatura.ImportParameters(certificado.PublicKey.ExportParameters(False))
Return algoritmoAssinatura.VerifyHash(hash,
CryptoConfig.MapNameToOID("SHA1"), _
assinatura)
End Function
Por último, o método CarregaCertificado usado aqui não carrega
o certificado pelo seu thumbprint,
como vimos na assinatura, mas sim a partir de um arquivo:
Function CarregaCertificado(ByVal
nomeArquivo As String)
As X509Certificate
Dim certificado As
X509Certificate = X509Certificate.CreateCertFromFile(nomeArquivo)
Return certificado
End Function
E pronto. Teste gerando a assinatura para uma string, e fazendo pequenas
modificações na assinatura ou na string. Até mesmo a menor modificação torna a
assinatura inválida, pois a função de hash produz resultados bastante
diferentes mesmo com modificações pequenas na informação na qual o hash é
calculado.
Alguns pontos devem ser
destacados sobre o exemplo apresentado aqui.
§
Qualquer
operação de criptografia exige seu preço em termos de uso de CPU. E a
criptografia assimétrica é o tipo mais pesado de criptografia, no tocante a
processamento. Então um teste de carga antes de a solução entrar em
produção se torna mandatório.
§
Para
uso especificamente em web services: a biblioteca WSE foi feita para trabalhar
principalmente a nível do protocolo SOAP, que é o protocolo de codificação das
mensagens trocadas por um web service e seu cliente. Realmente, se você usar no
seu web service exatamente o que eu fiz aqui (que é o que eu estou fazendo hoje),
você está executando o mesmo trabalho duas vezes, pois a informação a ser
assinada tem que ser serializada em um array de bytes, depois a mesma
informação tem que ser serializada como XML para envio dentro da mensagem SOAP.
Eu estou razoavelmente convencido de que existe uma forma mais otimizada de
fazer o que eu estou fazendo aqui, mas de novo a pressa foi inimiga da
perfeição. Um lema que tristemente se repetiu durante este projeto foi
"Pode não ser a melhor forma, mas funciona!". Bem, é um risco que você
sempre corre quando adota pela primeira vez uma tecnologia em um projeto.
Apesar de tudo, a assinatura digital
é um mecanismo extremamente interessante para qualquer tipo de interação em
e-commerce, e mesmo para sistemas corporativos internos, e a sua tendência é se
popularizar, com o lançamento do e-CPF e e-CNPJ criados pela Receita Federal.
Mais sobre este assunto no próprio site da Receita (http://www.receita.fazenda.gov.br/).
Um outro site muito bom é o da Autoridade
Certificadora Raiz da ICP-Brasil (Infra-estrutura de Chaves Públicas -
Brasil), que contém inclusive links sobre legislação, algo que você deve rever
caso deseje proteger suas operações usando assinaturas digitais.