sábado, 17 de setembro de 2011

Calculando MD5 e SHA1 em Java

Hashes são extremamente úteis para representar em uma pequena cadeia de bits uma outra cadeia de bits que pode ser bem maior. Podemos considerá-lo uma forma de criptografia assimétrica, uma vez que não é possível (a não ser por meio de algoritmos de força bruta) recuperar a cadeia original a partir do hash.

Existem diversos algoritmos para o cálculo de hashes, e em Java utilizamos a classe MessageDigest para recuperar a implementação desejada. Por exemplo, para calcular o MD5 de uma string, podemos utilizar:

MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update("string original".getBytes());
byte[] digest = md5.digest();

Para exibir os dados como string, recurso util quando você pretende utilizar o MD5 para conferir se todos os bytes de um arquivo foram baixados, basta formatá-lo com:

String.format("%1$032x", new BigInteger(1, digest));

O resultado é exatamente o hash MD5 da string:

~$ echo "string original" | md5sum
3ced9d1f470cd18f06a8b6492d3ea94e  -

Neste exemplo, utilizamos a string de formatação "%1$032x", porque queremos o resultado em hexadecimal, alinhado em 32 casas à esquerda. Construímos um BigInteger positivo, com os bits resultantes da soma, e isso nos permite converter os bytes do array para hexadecimal de forma simples.

Podemos fazer de forma semelhante ao calcular o SHA1:

MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
sha1.update("string original".getBytes());
byte[] digest = sha1.digest();
String.format("%1$040x", new BigInteger(1, digest));

Generalizando, você pode também utilizar os seguinte métodos:

public static String computeHash(String data, String algoritm,
        Integer resultSize) {
    try {
        MessageDigest digest = MessageDigest.getInstance(algoritm);
        digest.update(data.getBytes());
        return hexdigits(digest.digest(), resultSize);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Unable to hash with " + algoritm);
    }
}

public static String hexdigits(byte[] data, Integer resultSize) {
    return String
            .format("%1$0" + resultSize + "x", new BigInteger(1, data));
}

Até a próxima dica!