¿Alguien puede explicar la conversión de una matriz de bytes a una cadena hexadecimal?

StackOverflow https://stackoverflow.com/questions/1043276

  •  20-08-2019
  •  | 
  •  

Pregunta

Recientemente comencé a buscar el hash MD5 (en Java) y, aunque he encontrado algoritmos y métodos para ayudarme a lograrlo, me pregunto cómo funciona realmente.

Por un lado, encontré lo siguiente en esta URL :

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }
    return buf.toString();
}

No he encontrado la necesidad de usar bit-shift en Java, así que estoy un poco oxidado en eso. Alguien lo suficientemente amable como para ilustrar (en términos simples) ¿cómo exactamente hace la conversión el código anterior? " > > > " ;?

También encontré otras soluciones en StackOverflow, como aquí y aquí , que utiliza BigInteger en su lugar:

try {
   String s = "TEST STRING";
   MessageDigest md5 = MessageDigest.getInstance("MD5");
   md5.update(s.getBytes(),0,s.length());
   String signature = new BigInteger(1,md5.digest()).toString(16);
   System.out.println("Signature: "+signature);

} catch (final NoSuchAlgorithmException e) {
   e.printStackTrace();
}

¿Por qué funciona eso también y de qué manera es más eficiente?

Gracias por su tiempo.

¿Fue útil?

Solución

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {

Hasta este punto ... solo configuración básica e inicio de un bucle para recorrer todos los bytes de la matriz

        int halfbyte = (data[i] >>> 4) & 0x0F;
Los

bytes cuando se convierten a hexadecimal son dos dígitos hexadecimales u 8 dígitos binarios, dependiendo de la base en la que lo mire. con 0000 1111 para que el resultado sea un número entero igual a los 4 bits altos del byte (primer dígito hexadecimal).

Digamos que 23 fue una entrada, esto es 0001 0111 en binario. El cambio hace y lógico Y lo convierte en 0000 0001.

        int two_halfs = 0;
        do {

Esto solo configura el ciclo do / while para que se ejecute dos veces

            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

Aquí mostramos el dígito hexadecimal real, básicamente usando el cero o un carácter como punto de partida y desplazándonos al carácter correcto. La primera instrucción if cubre todos los dígitos 0-9, y la segunda cubre todos los dígitos 10-15 (a-f en hexadecimal)

Nuevamente, usando nuestro ejemplo 0000 0001 en decimal es igual a 1. Quedamos atrapados en el bloque if superior y agregamos 1 al carácter '0' para obtener el carácter '1', agregue eso a la cadena y avancemos .

                halfbyte = data[i] & 0x0F;

Ahora configuramos el número entero para igualar los bits bajos del byte y repetir.

Nuevamente, si nuestra entrada fue 23 ... 0001 0111 después del AND lógico se convierte en 0000 0111, que es 7 en decimal. Repita la misma lógica que la anterior y se muestra el carácter '7'.

            } while(two_halfs++ < 1);

Ahora solo pasamos al siguiente byte en la matriz y repetimos.

        }
    return buf.toString();
}

Para responder a su próxima pregunta, la API de Java ya tiene una utilidad de conversión de base integrada en BigInteger. Consulte el toString (int radix ) documentación.

Sin conocer la implementación utilizada por la API de Java, no puedo decir con certeza, pero estaría dispuesto a apostar que la implementación de Java es más eficiente que el primer algoritmo algo simple que publicaste.

Otros consejos

Para responder este bit:

  

¿Por qué funciona eso también?

No lo hace. Al menos, no de la misma manera que lo hace la versión en bucle. El nuevo BigInteger (...). toString (16) no mostrará los ceros a la izquierda, como lo hará la versión anterior. Por lo general, para algo como escribir una matriz de bytes (especialmente una que represente algo así como un hash), desearía una salida de longitud fija, por lo que si desea usar esa versión, tendrá que rellenarla adecuadamente.

Para obtener una explicación detallada sobre el desplazamiento de bits, consulte las respuestas en la siguiente pregunta SO ¿Qué son los operadores de cambio bit a bit (bit-shift) y cómo funcionan?

Parece intentar convertir un solo byte en un número menor que 16, al hacerlo, puede determinar fácilmente qué caracteres representa ese byte con el código

  if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

Esta es una respuesta simplista, pero de todos modos no soy tan brillante = D

Estas cosas no tienes que escribirlas tú mismo, porque ya están escritas en apache-commons-codec:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(byte[] array)

Hay muchos métodos más útiles en la clase Hex .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top