Cadena Java a SHA1

158

Estoy tratando de hacer un simple convertidor de Cadena a SHA1 en Java y esto es lo que tengo ...

public static String toSHA1(byte[] convertme) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    } 
    return new String(md.digest(convertme));
}

Cuando lo paso toSHA1("password".getBytes()), [�a�ɹ??�%l�3~��.sé que probablemente sea una solución de codificación simple como UTF-8, pero ¿alguien podría decirme qué debo hacer para obtener lo que quiero 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? ¿O estoy haciendo esto completamente mal?

Brian
fuente
El algoritmo no tiene SHA1guión, no sé si eso hará la diferencia.
The Scrum Meister
Es una buena práctica especificar la codificación de caracteres cuando llama getBytes(), por ejemplo, usetoSHA1("password".getBytes("UTF-8"))
Qwerky
posible duplicado de Java calcular un sha1 de una cadena
Tulains Córdova

Respuestas:

183

ACTUALIZACIÓN
Puede usar Apache Commons Codec (versión 1.7+) para hacer este trabajo por usted.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Gracias a @ Jon Onstott por esta sugerencia.


Respuesta anterior
Convierta su matriz de bytes en una cadena hexadecimal. Real's How To te dice cómo .

return byteArrayToHexString(md.digest(convertme))

y (copiado de Real's How To)

public static String byteArrayToHexString(byte[] b) {
  String result = "";
  for (int i=0; i < b.length; i++) {
    result +=
          Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
  }
  return result;
}

Por cierto, puede obtener una representación más compacta usando Base64. Apache Commons Codec API 1.4 , tiene esta buena utilidad para eliminar todo el dolor. consulte aquí

Nishant
fuente
44
base64 y sha1 son muy diferentes, no los sugiera como alternativas.
Ryan A.
13
@RyanA .: Según tengo entendido, sugiere base64 como una alternativa a la codificación hexadecimal del hash SHA1 (no como una alternativa a SHA1 por completo).
helmbert
Todavía no lo he probado, pero ¿te gustaría explicar cómo funciona?
Jivay
11
¿Por qué no usar una biblioteca como en DigestUtils.sha1Hex("my string")lugar de reinventar la rueda (aunque es interesante saber cómo convertir a hexágono a mano)?
Jon Onstott
3
Porque cuando se escribió esta respuesta, DigestUtils (1.7 se lanzó en septiembre de 2012) no tenía esa característica. Gracias por señalar esto. +1
Nishant
67

Esta es mi solución de convertir cadenas a sha1. Funciona bien en mi aplicación de Android:

private static String encryptPassword(String password)
{
    String sha1 = "";
    try
    {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes("UTF-8"));
        sha1 = byteToHex(crypt.digest());
    }
    catch(NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch(UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    return sha1;
}

private static String byteToHex(final byte[] hash)
{
    Formatter formatter = new Formatter();
    for (byte b : hash)
    {
        formatter.format("%02x", b);
    }
    String result = formatter.toString();
    formatter.close();
    return result;
}
petrnohejl
fuente
77
Es posible que desee especificar que esto es java.util.Formatter y necesita un formatter.close () al final para evitar advertencias.
Eric Chen
¿Shoudln't encryptPassword("test")y echo test|sha1sumen la salida del terminal de Linux el mismo resultado? Ellos no.
Tulains Córdova
@ TulainsCórdova Con respecto a la invocación de la consola: si utiliza echo test, se canalizará la salida que incluye un salto de línea sha1sum. Si desea hacer un hash de una cadena sin saltos de línea, puede usar echo -n test | sha1sum. El -nparámetro hace echoomitir el salto de línea.
MrSnrub
Menos a la pregunta, pero más en general: encryptPassword()parece que se usa para almacenar datos de autenticación. Tenga en cuenta que su codificación es vulnerable a los ataques de diccionario, ya que no se aplica ninguna inicialización. ¡Compruebe su entorno de seguridad si esto es un problema para su aplicación!
EagleRainbow
54

Usando la clase Hashing de guayaba :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Jan Schaefer
fuente
1
Es posible que esta respuesta necesite una actualización, ya que ahora generará una advertencia de que el Hashing es inestable.
Semir Deljić
32

SHA-1 (y todos los demás algoritmos de hashing) devuelven datos binarios. Eso significa que (en Java) producen a byte[]. Ese byteconjunto no representa ningún carácter específico, lo que significa que no puede simplemente convertirlo en algo Stringcomo lo hizo.

Si necesita un String, entonces debe formatearlo byte[]de una manera que pueda representarse como un String(de lo contrario, solo mantenga el byte[]alrededor).

Dos formas comunes de representar caracteres arbitrarios byte[]como imprimibles son BASE64 o cadenas hexadecimales simples (es decir, cada una representada bytepor dos dígitos hexadecimales). Parece que estás tratando de producir una cadena hexadecimal.

También hay otro inconveniente: si desea obtener el SHA-1 de un Java String, entonces debe convertirlo Stringen un byte[]primero (ya que la entrada de SHA-1 también es una byte[]). Si simplemente usa myString.getBytes()lo que mostró, usará la codificación predeterminada de la plataforma y, como tal, dependerá del entorno en el que lo ejecute (por ejemplo, podría devolver datos diferentes según el idioma / configuración regional de su sistema operativo).

Una solución mejor es para especificar la codificación a utilizar para la String-a-- byte[]conversión como esto: myString.getBytes("UTF-8"). Elegir UTF-8 (u otra codificación que pueda representar todos los caracteres Unicode) es la opción más segura aquí.

Joachim Sauer
fuente
27

Esta es una solución simple que se puede usar al convertir una cadena a un formato hexadecimal:

private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest crypt = MessageDigest.getInstance("SHA-1");
    crypt.reset();
    crypt.update(password.getBytes("UTF-8"));

    return new BigInteger(1, crypt.digest()).toString(16);
}
Nikita Koksharov
fuente
Advertencia: la generación de hash es incorrecta para los hash que comienzan con '0'. Recibirá una cadena con 39 caracteres.
Phil
@philn ¿podría sugerir una solución?
Nikita Koksharov el
1
Supongo que si crea un número entero grande a partir de un byte [] con suficientes ceros a la izquierda, estos 0 se pierden. Por lo tanto, la representación de cadena hexadecimal "0" no estará allí, lo que dará lugar a un hash con 39 caracteres o incluso menos. Utilicé la solución de petrnohejls anterior y funciona bien ...
Phil
25

Simplemente use la biblioteca de códec de apache commons. Tienen una clase de utilidad llamada DigestUtils

No es necesario entrar en detalles.

DaTroop
fuente
51
No estoy de acuerdo, entrar en detalles es algo así como el punto completo
autorización
12
La pregunta es si tienes tiempo para entrar en detalles o no. Por lo general, el objetivo es hacerlo a tiempo. No todos son estudiantes o tienen el lujo de aprender todos los detalles.
DaTroop
DigestUtils devuelve una matriz de bytes para obtener la representación de cadena que necesita para ejecutarla a través de Hex.encodeHexString. Java: es 2014 y todavía no tenemos un método sha de un solo paso
ryber
55
Método SHA-1 de un paso:; String result = DigestUtils.sha1Hex("An input string")o)
Jon Onstott
18

Como se mencionó anteriormente, use el códec de apache commons. También lo recomiendan los chicos de Spring (ver DigestUtils en Spring doc). P.ej:

DigestUtils.sha1Hex(b);

Definitivamente no usaría la respuesta mejor calificada aquí.

kazuar
fuente
7

No se imprime correctamente porque necesita usar la codificación Base64. Con Java 8 puede codificar utilizando la clase de codificador Base64 .

public static String toSHA1(byte[] convertme) {
    md = MessageDigest.getInstance("SHA-1");
    return Base64.getEncoder().encodeToString((md.digest(convertme));
}

Resultado

Esto le dará su salida esperada de 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

Eduardo Dennis
fuente
1
@Devenv es SHA-1, los tres puntos significa que mantendrá su código original que se convierte en sha1. El problema original de OP era al imprimir la cadena correctamente.
Eduardo Dennis
4

El resumen del mensaje (hash) es byte [] in byte [] out

Un resumen de mensaje se define como una función que toma un conjunto de bytes sin formato y devuelve un conjunto de bytes sin formato (también conocido como byte[]). Por ejemplo, SHA-1 (Algoritmo de hash seguro 1) tiene un tamaño de resumen de 160 bits o 20 bytes. Las matrices de bytes sin procesar generalmente no se pueden interpretar como codificaciones de caracteres como UTF-8 , porque no todos los bytes en cada orden son legales. Entonces convertirlos a a Stringcon:

new String(md.digest(subject), StandardCharsets.UTF_8)

puede crear algunas secuencias ilegales o tiene punteros de código para asignaciones Unicode indefinidas :

[�a�ɹ??�%l3~��.

Codificación de binario a texto

Para eso se utiliza la codificación de binario a texto . Con los hashes, el que más se usa es la codificación HEX o Base16 . Básicamente, un byte puede tener el valor de 0a 255(o -128con 127signo) que es equivalente a la representación HEX de 0x00- 0xFF. Por lo tanto, el hexadecimal duplicará la longitud requerida de la salida, eso significa que una salida de 20 bytes creará una cadena hexadecimal de 40 caracteres de longitud, por ejemplo:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Tenga en cuenta que no es necesario utilizar la codificación hexadecimal. También podrías usar algo como base64 . A menudo se prefiere el maleficio porque es más fácil de leer por los humanos y tiene una longitud de salida definida sin la necesidad de relleno.

Puede convertir una matriz de bytes a hexadecimal con la funcionalidad JDK sola:

new BigInteger(1, token).toString(16)

Sin embargo, BigIntegertenga en cuenta que interpretará la matriz de bytes dada como un número y no como una cadena de bytes. Eso significa que no se generarán ceros a la izquierda y la cadena resultante puede ser más corta que 40 caracteres.

Uso de bibliotecas para codificar en HEX

Ahora podría copiar y pegar un método de byte a hex no probado desde Stack Overflow o usar dependencias masivas como Guava .

Para tener una solución para la mayoría de los problemas relacionados con los bytes, implementé una utilidad para manejar estos casos: bytes-java (Github)

Para convertir su conjunto de bytes de resumen de mensaje, simplemente puede hacer

String hex = Bytes.wrap(md.digest(subject)).encodeHex();

o simplemente puedes usar la función integrada de hash

String hex =  Bytes.from(subject).hashSha1().encodeHex();
Patrick Favre
fuente
2

Base 64 Representación de SHA1Hash:

String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));
DOGUILLO
fuente
1

La razón por la que esto no funciona es que cuando llamas String(md.digest(convertme)), le estás diciendo a Java que interprete una secuencia de bytes encriptados como una cadena. Lo que quieres es convertir los bytes en caracteres hexadecimales.

Zarkonnen
fuente
0

Convierta la matriz de bytes en una cadena hexadecimal.

public static String toSHA1(byte[] convertme) {
    final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    byte[] buf = md.digest(convertme);
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i) {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}
abhihere
fuente