Criptografia em Java - Um cuidado a ser tomado
Autor: Welder | Publicado: 2011-06-08T07:42:00-07:00

Esta postagem tem dois objetivos, o primeiro é debater um tema altamente importante se tratando de segurança da informação que é a criptografia de dados, tal debate será acrescido de modos de implementação da criptografia no mundo java; o segundo é um alerta sobre um possível problema na implementação de criptografia utilizando java.

Começando então pela primeira parte que é o debate sobre a criptografia. Em um domínio mais amplo, creio que a criptografia pode ser considerada uma ciência a parte e é utilizada para cifrar a linguagem convencional em uma linguagem de códigos onde somente os que conhecem tal código poderão saber do que se trata. Por exemplo, posso agora criar um código com as seguintes correspondências de palavras:

ciência = %ac&amp;8j

da = 154/*ae2

computação = ',la7¨&amp;*

Ao transmitir a seguinte mensagem criptografada: %ac&amp;8j 154/*ae2" ',la7¨&amp;* ninguém que não conheça tal código que foi determinado entenderá o que quer dizer tal mensagem. Bom, até aqui foi uma ilustração bem ampla de criptografia só para que esse termo tenha um significado mais sólido nas próximas utilizações.


Voltando para o mundo da computação, com a popularização dos computadores e da internet, surgiu uma grande preocupação com a segurança das informações manipuladas "digitalmente" e então a computação apropriou-se da criptografia para ajudar nessa tarefa de garantir a segurança da informação.

Afunilando um pouco mais o amplo domínio que estudar a criptografia oferece, vamos tratar de algoritmos de criptografia mais conhecidos e a implementação de tais algoritmos utilizando Java para que a postagem não fique demasiadamente extensa.


Existem algoritmos de criptografia que fornecem o modo como as informações serão criptografadas, dentre os mais famosos, podem ser destacados o SHA-1 e o MD5 que já possuem implementações pré-determinadas em algumas linguagens de programação como o PHP ou até mesmo em clientes de SGBD como no Postgre SQL por exemplo.

No java não existe uma implementação específica para cada algoritmo de criptografia, mas sim uma implementação genérica onde o Algoritmo que será utilizado é fornecido como parâmetro. A implementação de Algoritmos de criptografia no Java é facilitada pela classe java.security.MessageDigest. Para que seja ilustrado o modo de utilização de tal classe, nada como um trecho de código:


&nbsp;&nbsp;&nbsp;&nbsp; public static String criptografar(String valor) throws NoSuchAlgorithmException {
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; //Note que o Algoritmo de criptografia é fornecido por parâmetro.
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //Nesse caso 'MD5' poderia ser 'SHA1'.
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  MessageDigest md = MessageDigest.getInstance("MD5");

&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //A instância de MessageDigest foi atualizada com o valor da String&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //fornecida como parâmetro, neste caso foi fornecida a codificação&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //para obtenção dos bytes da String, mas esse parâmetro é opcional.
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  md.update(valor.getBytes("UTF8"));

&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //É nesse ponto que é gerado um array de bytes contendo o resultado&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //da criptografia utilizando o Algoritmo fornecido.
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  byte[] s = md.digest();

&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //Como precisamos do hash em String precisamos executar os seguintes
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  //três passos básicos para tal necessidade:
               &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  BigInteger hash1 = new BigInteger(1, s);&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  String hash = hash1.toString(16);&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  while(hash.length() &lt; 32) {&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  hash = "0" + hash;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;  }

&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return hash;
&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;





Bom, essa implementação é relativamente simples e os comentários no código explicam o funcionamento.

Portanto caso esse método seja invocado passando uma String qualquer, irá retornar o uma String resultado da criptografia que não é chamada de hash. Por exemplo:

&nbsp;&nbsp;&nbsp;&nbsp; "computação" = "864a5250426d8dbcd058b16a2098186f"

&nbsp;&nbsp;&nbsp;&nbsp; "criptografia" = "97c6105c1d97d600ec16ab4abace6d4c"


O hash gerado pelos algoritmos de criptografia possuem diversas utilizações no campo da computação, principalmente na área de segurança da informação. Por exemplo, ao invés de armazenar a senha de usuários no banco de dados, é mais seguro armazenar o hash da senha, mas enfim, não é bem esse o foco desta postagem.

E como já havia sido apresentado no início da postagem ainda temos mais uma segunda parte que é um cuidado com a implementação da criptografia no Java.

Note que o array de bytes gerado no md.digest() é convertido para um BigInteger isso faz com que hashs gerados iniciando em "0" ignorem este caractere, já que se tratando de números (BigInteger) não faz sentido considerar o zero à esquerda, por isso que foi acrescentado o while() logo na sequência acrescentando os zeros que possivelmente foram removidos, mais um detalhe é que o hash do md5 seguindo essa implementação sempre possui 32 caracteres, por isso o loop do while vai até esse número (o SHA1, por exemplo, gera os hashs sempre com 40 caracteres).

Já ouvi vários relatos de o hash gerado pelo Java ser diferente do hash gerado pelo PHP utilizando o mesmo Algoritmo de criptografia e na maioria dos casos, a causa do problema estava nessa exclusão dos zeros a esquerda no BigInteger.

É isso, conhecida a criptografia, modo de implementação e a solução para uma possível falha, a criptografia pode ser explorada utilizando o Java das mais diversas maneiras.