Hash String a través de SHA-256 en Java

111

Al mirar por aquí, así como en Internet en general, he encontrado Bouncy Castle . Quiero usar Bouncy Castle (o alguna otra utilidad disponible gratuitamente) para generar un hash SHA-256 de una cadena en Java. Al mirar su documentación, parece que no puedo encontrar ningún buen ejemplo de lo que quiero hacer. ¿Alguien aquí puede ayudarme?

knpwrs
fuente

Respuestas:

264

Para aplicar hash a una cadena, use la clase MessageDigest incorporada :

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.math.BigInteger;

public class CryptoHash {
  public static void main(String[] args) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String text = "Text to hash, cryptographically.";

    // Change this to UTF-16 if needed
    md.update(text.getBytes(StandardCharsets.UTF_8));
    byte[] digest = md.digest();

    String hex = String.format("%064x", new BigInteger(1, digest));
    System.out.println(hex);
  }
}

En el fragmento anterior, digestcontiene la cadena hash y hexcontiene una cadena ASCII hexadecimal con relleno de ceros a la izquierda.

Brendan Long
fuente
2
@Harry Pham, el hash siempre debe ser el mismo, pero sin más información sería difícil decir por qué obtienes otros diferentes. Probablemente debería abrir una nueva pregunta.
Brendan Long
2
@Harry Pham: Después de llamar, digestse restablece el estado interno; así que cuando lo vuelves a llamar sin actualizar antes, obtienes el hash de la cadena vacía.
Debilski
3
@BrendanLong Ahora, ¿cómo recuperar digesta String de nuevo?
Sajad
1
@Sajjad Usa tu función de codificación base64 favorita. Personalmente, me gusta el de Apache Commons .
Brendan Long
1
@ Adam No, esto es engañoso. Llamar a ´toString´ en una matriz de bytes produce un resultado diferente al de convertir el contenido de la matriz de bytes a String, la respuesta de Brendan Longs es más apropiada
max.mustermann
30

Esto ya está implementado en las bibliotecas en tiempo de ejecución.

public static String calc(InputStream is) {
    String output;
    int read;
    byte[] buffer = new byte[8192];

    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        while ((read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
        byte[] hash = digest.digest();
        BigInteger bigInt = new BigInteger(1, hash);
        output = bigInt.toString(16);
        while ( output.length() < 32 ) {
            output = "0"+output;
        }
    } 
    catch (Exception e) {
        e.printStackTrace(System.err);
        return null;
    }

    return output;
}

En un entorno JEE6 + también se podría utilizar JAXB DataTypeConverter :

import javax.xml.bind.DatatypeConverter;

String hash = DatatypeConverter.printHexBinary( 
           MessageDigest.getInstance("MD5").digest("SOMESTRING".getBytes("UTF-8")));
apilador
fuente
3
esta versión tiene un error (al menos a partir de hoy y con java8 @ win7). intente hash '1234'. el resultado debe comenzar con '03ac67 ...' pero en realidad comienza con '3ac67 ...'
Chris
@ Chris gracias, arreglé esto aquí, pero encontré una mejor solución, mi favorita en esto es actualmente: stackoverflow.com/questions/415953/…
apilador
1
Para los nuevos lectores de este antiguo hilo: el favorito (MD5-hash) al que hace referencia @stacker ahora se considera inseguro ( en.wikipedia.org/wiki/MD5 ).
mortensi
1
La condición debe ser output.length () <64, no 32.
Sampo
¿Cómo podemos comparar dos valores hash diferentes creados con la misma sal? Supongamos que uno proviene del inicio de sesión del formulario (es necesario utilizar un hash con Salt antes de comparar) y otro proviene de db y luego, ¿cómo podemos comparar estos dos?
Pra_A
16

No necesita necesariamente la biblioteca BouncyCastle. El siguiente código muestra cómo hacerlo usando la función Integer.toHexString

public static String sha256(String base) {
    try{
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(base.getBytes("UTF-8"));
        StringBuffer hexString = new StringBuffer();

        for (int i = 0; i < hash.length; i++) {
            String hex = Integer.toHexString(0xff & hash[i]);
            if(hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }

        return hexString.toString();
    } catch(Exception ex){
       throw new RuntimeException(ex);
    }
}

Un agradecimiento especial al usuario1452273 de esta publicación: ¿Cómo hacer hash en alguna cadena con sha256 en Java?

¡Sigan con el buen trabajo!

Latigazo
fuente
9

Cuando use códigos hash con cualquier proveedor jce, primero intente obtener una instancia del algoritmo, luego actualícelo con los datos que desea aplicar con el hash y, cuando haya terminado, llame a digest para obtener el valor hash.

MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.update(in.getBytes());
byte[] digest = sha.digest();

puede utilizar el resumen para obtener una versión codificada en base64 o hexadecimal según sus necesidades

Nikolaus Gradwohl
fuente
Por curiosidad, ¿puede ir directamente a digest()la matriz de bytes de entrada, omitiendo update()?
ladenedge
Una cosa que noté es que esto funciona con "SHA-256" mientras que "SHA256" arroja una NoSuchAlgorithmException. No es problema.
knpwrs
9
Según mi comentario a Brandon, no lo use String.getBytes()sin especificar una codificación. Actualmente, este código puede dar diferentes resultados en diferentes plataformas, lo que es un comportamiento roto para un hash bien definido.
Jon Skeet
@ladenedge sí, si su cadena es lo suficientemente corta @KPthunder, su @Jon Skeet correcto depende del contenido de la cadena, pero sí, agregue una cadena de codificación getBytes para estar en el lado guardado
Nikolaus Gradwohl
7

Java 8: Base64 disponible:

    MessageDigest md = MessageDigest.getInstance( "SHA-512" );
    md.update( inbytes );
    byte[] aMessageDigest = md.digest();

    String outEncoded = Base64.getEncoder().encodeToString( aMessageDigest );
    return( outEncoded );
Mike Dever
fuente
5

Supongo que está utilizando una versión de Java relativamente antigua sin SHA-256. Por lo tanto, debe agregar el proveedor de BouncyCastle a los 'Proveedores de seguridad' ya proporcionados en su versión de Java.

    // NEEDED if you are using a Java version without SHA-256    
    Security.addProvider(new BouncyCastleProvider());

    // then go as usual 
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String text = "my string...";
    md.update(text.getBytes("UTF-8")); // or UTF-16 if needed
    byte[] digest = md.digest();
obe6
fuente
+1 por mencionar BouncyCastle, que agrega bastantes MessageDigests nuevos en comparación con la selección relativamente insignificante disponible en el JDK.
mjuarez
0
return new String(Hex.encode(digest));
Kay
fuente
4
Sin información de paquete / proveedor, la clase "Hex" no es muy útil. Y si eso es Hex de Apache Commons Codec, lo usaría return Hex.encodeHexString(digest)en su lugar.
eckes
0

Usando Java 8

MessageDigest digest = null;
try {
    digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String encoded = DatatypeConverter.printHexBinary(hash);        
System.out.println(encoded.toLowerCase());
MST
fuente
-1

Esto funcionará con el siguiente paquete "org.bouncycastle.util.encoders.Hex"

return new String(Hex.encode(digest));

Está en un frasco de castillo hinchable.

Shrikant Karvade
fuente