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.
09.07.2008 @ 15:26 \03\Wed, 09 Jul 2008 15:26:07 +0000\07 +0000 UTC at 3:26 pm
Apoco necesitaban un post para hacerlo así, osea hellooooo.
10.12.2008 @ 01:05 \01\Wed, 10 Dec 2008 01:05:16 +0000\16 +0000 UTC at 1:05 am
Esta excelente brother lo necesitaba.
salu2
18.05.2009 @ 21:44 \09\Mon, 18 May 2009 21:44:02 +0000\02 +0000 UTC at 9:44 pm
Acabas de solucionarme un problema que me traía de cabeza. Gracias
30.08.2009 @ 21:11 \09\Sun, 30 Aug 2009 21:11:31 +0000\31 +0000 UTC at 9:11 pm
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
07.09.2009 @ 14:46 \02\Mon, 07 Sep 2009 14:46:22 +0000\22 +0000 UTC at 2:46 pm
para qué necesitas ordenar y comparar valores hash? Al estar “hasheados” no creo que sea de mucha utilidad compararlos u ordenarlos.
De cualquier forma creo que es más eficiente si los comparas como arreglo de bytes.
01.08.2011 @ 08:43 \08\Mon, 01 Aug 2011 08:43:23 +0000\23 +0000 UTC at 8:43 am
comparas se debería a que quiere Decryptar el hash, en este caso sacar algún salt como el de facebook o hotmail…o una aplicacion no hacker seria demostrar alguna vulnerabilidad o que para un string existen dos hash, o un hash da dos strings etc
23.03.2010 @ 19:34 \07\Tue, 23 Mar 2010 19:34:50 +0000\50 +0000 UTC at 7:34 pm
Hola, será que el codigo se puede cambiar para que reciba un solo hexadecimal que esta almacenado en un String. hgracias
23.03.2010 @ 21:16 \09\Tue, 23 Mar 2010 21:16:19 +0000\19 +0000 UTC at 9:16 pm
este codigo se puede transformar para utilizarlo con un hexadecimal que esta en un string.
15.08.2011 @ 20:00 \08\Mon, 15 Aug 2011 20:00:06 +0000\06 +0000 UTC at 8:00 pm
esta muy bueno pero como sería para descryptarlo..
31.03.2012 @ 11:12 \11\Sat, 31 Mar 2012 11:12:43 +0000\43 +0000 UTC at 11:12 am
Me ha encantado, you have save my day.