MD5 Hexadecimal Hashing en Java

09.07.2008 @ 13:14 \01\Wed, 09 Jul 2008 13:14:47 +0000\47 +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.

10 Responses to “MD5 Hexadecimal Hashing en Java”

  1. Manuel Says:

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

  2. Elthamare Says:

    Acabas de solucionarme un problema que me traía de cabeza. Gracias🙂

  3. Fernando Says:

    Hey, muchas gracias por el post, de verdad que me ayudado bastante (y eso q ya es algo “antiguo” XD). Aprovecho para pedirte un consejito, si quisiese comparar los valores hash obtenidos para ordenarlos, por ejemplo, ¿qué sería más recomendable y/o exacto: comparar los valores ya en STRING o compararlos cuando recién son BYTE[]?.
    Gracias

  4. Diego Says:

    Hola, será que el codigo se puede cambiar para que reciba un solo hexadecimal que esta almacenado en un String. hgracias

  5. Diego Says:

    este codigo se puede transformar para utilizarlo con un hexadecimal que esta en un string.


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: