MD5 Hexadecimal Hashing en Java

09.07.2008 @ 13:14 hrs +0000 UTC

Digamos que necesitas convertir un arreglo de byte a hexadecimal. Tal vez porque estás haciendo password hashing MD5 (no guardas los passwords de tus usuarios en vil texto verdad??). Seguramente pensaste que alguna clase “default” (String, Double, Long, etc) tendría un método para hacer la conversión. En efecto, esas clases derivadas de Number tienen un método toHexString.

Listo! Ya tienes tu método para convertir a hexadecimal. Usarías entonces Integer.toHexString(int).

Lo harías así?

public String hash(String stringToHash) {
    final StringBuilder sb = new StringBuilder();
    try {
	final MessageDigest md=MessageDigest.getInstance("MD5");
	final byte[] bytes = md.digest(stringToHash.getBytes());
	String str=new String(bytes);
	final char[] chars=str.toCharArray();

	for (int i = 0; i < chars.length; i++) {
		sb.append(Integer.toHexString((int) chars[i]));
	}

} catch (NoSuchAlgorithmException e) {
	//Please handle me! PLEASE!
	} 

	return sb.toString();
}

Si tu respuesta fue “Sí, así lo haría”, déjame decirte que tu programa te explotará en la cara y perderás al menos una retina…. ok, no tanto así, pero el resultado que obtendrías no sería el correcto. El detalle está en que al hacer Integer.toHexString((int) chars[i]) vas a perder los ceros a la izquierda (si eres licenciado, en este caso los 0 a la izquierda son importantes).

Entonces dices “Ah ok, no hay problema, le ponemos ceros así:”

for (int i = 0; i < chars.length; i++) {
	sb.append(Integer.toHexString( 0x10000 | (int)chars[i]).substring(1));
}

O mejor así (sin for y sin necesidad de StringBuilder):

...
final MessageDigest md=MessageDigest.getInstance("MD5");
final byte[] bytes = md.digest(stringToHash.getBytes());
return new BigInteger(1,bytes).toString(16);

Sin embargo, sigue siendo ineficiente. Hay que crear una instancia BigInteger para después obtener el resultado como String con un radix de 16, al hacer la instancia de BigInteger suceden cosas más elaboradas en el background.  Si podemos obtener directamente los bytes, deberíamos de manipularlos directamente. Parecerá un poco más complicado, pero es más eficiente ya que se manipulan los bytes directamente.

private static final char[] HEXADECIMAL = { '0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    public  String hash(String stringToHash)  {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(stringToHash.getBytes());
            StringBuilder sb = new StringBuilder(2 * bytes.length);
            for (int i = 0; i < bytes.length; i++) {
                int low = (int)(bytes[i] & 0x0f);
                int high = (int)((bytes[i] & 0xf0) >> 4);
                sb.append(HEXADECIMAL[high]);
                sb.append(HEXADECIMAL[low]);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            //exception handling goes here
            return null;
        }
    }

There you go! De hecho si gogolean (aniadan este verbo en su diccionario) un poco, van a toparse con este tipo de soluciones (de hecho la última solución es casi “best practice) y con unas “soluciones” abominables

Hay que recordar aquí que este tipo de hashing no es para nada seguro ya que es relativamente sencillo convertir el valor que ha sido hasheado (otro verbo más para su diccionario) a su valor original. El hashing solamente se hace para no guardar los passwords en vil texto en la BD. Si desean mandar datos de manera segura hay que usar el SSL para encriptar verdaderamente la información.

La función del encoding, en este caso hexadecimal pero también el base64 es muy usado, es simplemente para no romperle su mandarina en gajos al protocolo http ya que se evita mandar caracteres inválidos en el request.

Si se preguntan por qué no hay una función embedida en Java para hacer esto, están en todo su derecho…

Por cierto el resultado de hacer un hashing a “123″ es: 202cb962ac59075b964b07152d234b70

Nota 1: StringBuilder hace lo mismo que StringBuffer pero no es thread safe, por lo tanto es más rápido. Úsalo bajo la supervisión de un adulto.

3 Responses to “MD5 Hexadecimal Hashing en Java”

  1. Manuel Says:

    Apoco necesitaban un post para hacerlo así, osea hellooooo.

  2. amerikano Says:

    Esta excelente brother lo necesitaba.

    salu2

  3. Elthamare Says:

    Acabas de solucionarme un problema que me traía de cabeza. Gracias :-)


Leave a Reply