VB.NET - Gerando Hash e comparando arquivos


Além de permitir a implementação da segurança em sua aplicação as classes do namespace System.Security.Cryptography provêm diversas funcionalidades relacionadas a criptografia e segurança para ser usada em sua aplicação.

Devido a extensão do assunto neste artigo irei compilar informações obtidas na web sobre os algoritmos de hash e apresentar um exemplo de utilização usando o hash para comparar arquivos. Esta é uma técnica que pode ser usada para verificar se o arquivo não foi alterado, por exemplo, durante uma transmissão. Você calcula o hash, transmite o arquivo, e calcula novamente o hash comparando com o hash original.

Nota: Um hash é uma seqüencia de letras ou números geradas por um algorítimo de Hash.

Na criptografia, o hash serve para garantir a integridade da mensagem, onde o gerador (ou emissor) da mensagem, submete-a a um algoritmo hash, o qual produzirá um valor hash (este é outro nome pelo qual é conhecido o MD).

Este valor é enviado junto com a mensagem para o destinatário. O destinatário fará a verificação da integridade da mensagem aplicando o mesmo algoritmo na mensagem original e obterá um valor hash que deverá ser igual ao valor hash gerado na origem.

Se for diferente, a mensagem foi alterada no "caminho" - é óbvio que a mensagem e o MD terão que ser encriptados para dificultar a intervenção de terceiros.(MD-message Digest
)

O algoritmo hash é composto por fórmulas matemáticas complexas, para poder garantir a irreversibilidade e a unicidade do MD gerado - textos diferentes não produzem o mesmo MD. A alteração de um simples bit na mensagem gera um MD completamente diferente e o valor de conferência ("check-sum") muda se um único bit for alterado, acrescentado ou retirado da mensagem.

O que é um algoritmo hash?
Uma função hash é uma equação matemática que utiliza texto (tal como uma mensagem de e-mail) para criar um código chamado message digest (resumo de mensagem). Alguns exemplos conhecidos de funções hash: MD4 (MD significa message digest), MD5 e SHS.

O tamanho do MD depende do algoritmo escolhido (MD1, MD2, ..., MD5 ou SHA1), que é medido em bits - por exemplo, o SHA1 é o mais recente dentre estes anteriores e gera um hash de 160 bits.
 
Uma função hash utilizada para autenticação digital deve ter certas propriedades que a tornem segura para uso criptográfico. Especificamente, deve ser impraticável encontrar:

- O Texto que dá um hash a um dado valor. Ou seja, mesmo que você conheça o MD-message digest, não conseguirá decifrar a mensagem.
- Duas mensagens distintas que dão um hash ao mesmo valor.

A capacidade de descobrir uma mensagem que dê um hash a um dado valor possibilita a um agressor substituir uma mensagem falsa por uma mensagem real que foi assinada.

Permite ainda que alguém rejeite de forma desleal uma mensagem, alegando que, na realidade, ele ou ela assinou uma mensagem diferente, dando um hash ao mesmo valor e violando assim a propriedade de não-repúdio das assinaturas digitais.

A capacidade de descobrir duas mensagens distintas que dêm um hash ao mesmo valor possibilita um tipo de ataque no qual alguém é induzido a assinar uma mensagem que dá um hash ao mesmo valor como sendo outra mensagem com um conteúdo totalmente diferente.

Varias funções hash têm sido desenvolvidas com objetivo de melhorar a versão anterior a fim de termos maior segurança e evitar que os ataques sejam bem sucedidos. As mais conhecidas são:

No namespace System.Security.Cryptography do .Net Framework temos as seguintes classes que implementam estes algoritmos:

Cada uma destas classes possui um modo diferente de calcular a assinatura e o tamanho da assinatura.

Além destas técnicas temos o MACHash , que é também um hash, implementado no namespace System.Security.Cryptography representados pelas classes HMACSHA1 e MACTripleDES.

A técnica acima é um Message Authentication Codes - MAC,  e, é similar ao conceito de um Hash. Ela difere na forma como é calculada pois usa uma chave/senha e, somente com a utilização desta chave/senha será possível calcular se um determinado Hash é autêntico.

MD5 – Message Digest Algorithm 5

Este é um algoritmo de encriptação desenvolvido em 1994 por Ronald Rivest, do MIT, que possui duas classes gerais de funcionamento - a encriptação "one-way" e "two-way".

Genericamente o MD5 mapeia uma string de texto de comprimento variável numa pequena string de texto encriptado e de tamanho fixo. No caso da versão 128 bits, a chave resultante será constituída por 16 caracteres.

A versão "two-way" é a mais utilizada, devido à sua confiabilidade para comunicações privadas; por exemplo: Usada para o cliente enviar ao servidor o número do Cartão de Crédito numa compra on-line, onde o servidor ao receber os dados encriptados é capaz de os desencriptar e obter a informação que foi transmitida inicialmente.

Deste modo, a informação torna-se ilegível para quem a conseguir interceptar no trajeto entre o cliente e o servidor.

Uma dos melhores maneiras de se proteger senhas de acesso a sistemas é armazenando-se apenas o MD no banco de dados. Toda vez que o usuário digitar a senha, o sistema deverá aplicar o algoritmo hash na mesma para obter o MD. Se um intruso conseguir acessar o banco de senhas, verá um monte de códigos que não farão sentido e o intruso não terá como aplicar algoritmos de reversão, pois este é um dos princípios do hash - a sua irreversibilidade

Criando um projeto para comparar arquivos

Após toda a teoria acima vamos praticar. Abra o VS.NET 2003 e crie um novo projeto do tipo Windows Application usando a linguagem VB.NET e dê a ele o nome de comparaArquivos.

Inclua no formulário padrão form1.vb os seguintes controles:

  • TextBox  - txtArquivo1

  • TextBox - txtArquivo2

  • Button - btnCompara

  • Label1 e Label2

 

Acrescente os seguintes imports ao seu projeto:

Imports System.IO
Imports
System.Security.Cryptography

A seguir no evento Click do botão inclua o seguinte código:

Private Sub btnCompara_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCompara.Click


Try

    If comparaArquivos.comparador(txtArquivo1.Text, txtArquivo2.Text) Then

         MsgBox("Os arquivos são iguais...", MsgBoxStyle.Information)

     Else

          MsgBox("Os arquivos NÃO são iguais...", MsgBoxStyle.Information)

     End If
Catch
ex As Exception

     MsgBox(ex.Message, MsgBoxStyle.Critical, "Erro")

End Try
 

End Sub

O código acima usa o método estático comparador de uma classe chamada comparaArquivos() e passa como argumento o nome e localização dos dois arquivos a comparar.

O bloco Try/Catch permite capturar qualquer exceção que for lançada pelo classe.

Vamos então criar a classe. Para isto inclua um novo modulo de classe no seu projeto. Isto pode ser feito diretamente no menu Project-> Add Class. Informe o nome da classe como comparaArquivos.vb.

A seguir inclua o código abaixo na classe:

Imports System.IO

Imports System.Security.Cryptography

Imports System.text

 

Public Class comparaArquivos

 

'Vetor de inicialização de criptografia

Public Shared IV() As Byte = {&H12, &H34, &H56, &H78, &H90, &HAB, &HCD, &HEF}

 

'Chave de criptografia

Public Shared key() As Byte = Encoding.UTF8.GetBytes("12345678")


'retorna true se os dois arquivos são iguais e falso se não forem
'funciona para arquivos textos e binários

Public Shared Function comparador(ByVal caminhoArq1 As String, ByVal caminhoArq2 As String) As Boolean


Dim
objMD5 As New MD5CryptoServiceProvider

Dim objEncoding As New ASCIIEncoding

'define as variáveis para cada arquivos
Dim
aArquivo1() As Byte, aArquivo2() As Byte
Dim
strConteudo1, strConteudo2 As
String

'Define um StreamReader
Dim objReader As StreamReader

Dim objFS As FileStream

Dim bResposta As Boolean

'verifica se o arquivo1 existe
If
Not File.Exists(caminhoArq1)
Then

    Throw New Exception(caminhoArq1 & " NÃO existe...")

End If


'verifica se o arquivo2 existe
If
Not File.Exists(caminhoArq2) Then
    Throw New Exception(caminhoArq2
& " NÃO existe...")
End If

Try

   'gera hash para o arquivo 1

   objFS = New FileStream(caminhoArq1, FileMode.Open)

   objReader = New StreamReader(objFS)

   aArquivo1 = objEncoding.GetBytes(objReader.ReadToEnd)

   strConteudo1 = objEncoding.GetString(objMD5.ComputeHash(aArquivo1))

   objReader.Close()

   objFS.Close()

 

   'gera hash para o arquivo 2

   objFS = New FileStream(caminhoArq2, FileMode.Open)

   objReader = New StreamReader(objFS)

   aArquivo2 = objEncoding.GetBytes(objReader.ReadToEnd)

   strConteudo2 = objEncoding.GetString(objMD5.ComputeHash(aArquivo2))
 

   'efetua a comparação dos arquivos

   bResposta = strConteudo1 = strConteudo2

   objReader.Close()

   objFS.Close()

 

   'libera objetos

   aArquivo1 = Nothing

   aArquivo2 = Nothing

Catch ex As Exception

    Throw ex

End Try
 

Return bResposta

End Function
End
Class

O método comparador foi definido como estático (shared) e por este motivo não precisamos criar uma instância da classe. Você pode usar a mesma classe para gerar um Hash para ser armazenado em um banco de dados para um senha informada pelo usuário.

O artigo tem como objetivo apresentar o conceito e dar um exemplo prático muito simples. Você pode incluir diversos melhoramentos no código e na implementação para isto basta avançar em seus conhecimentos.

Pegue o código do projeto aqui : comparaArquivos.zip

Até o próximo artigo .NET, e, acima de tudo, seja feliz...

Referências:

- VB - Criptografia e segurança. É possivel ?
-
VB.6 -  Criptografando e Decriptografando textos
-
VB.NET - Criptografando arquivos
-
ASP.NET - Usando Criptography Application Block.
-
http://pt.wikipedia.org/wiki/Criptografia


José Carlos Macoratti