String de hash via SHA-256 em Java

111

Ao procurar por aqui e também na Internet em geral, encontrei o Castelo Bouncy . Eu quero usar o Bouncy Castle (ou algum outro utilitário disponível gratuitamente) para gerar um SHA-256 Hash de uma String em Java. Olhando a documentação deles, não consigo encontrar bons exemplos do que quero fazer. Alguém aqui pode me ajudar?

knpwrs
fonte

Respostas:

264

Para hash uma string, use a classe interna MessageDigest :

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);
  }
}

No snippet acima, digestcontém a string com hash e hexuma string ASCII hexadecimal com preenchimento zero à esquerda.

Brendan Long
fonte
2
@Harry Pham, o hash deve ser sempre o mesmo, mas sem mais informações seria difícil dizer por que você está recebendo diferentes. Você provavelmente deve abrir uma nova pergunta.
Brendan Long
2
@Harry Pham: Depois de chamar digesto estado interno é reiniciado; portanto, quando você o chama novamente sem atualizar antes, obtém o hash da string vazia.
Debilski
3
@BrendanLong Agora, como recuperar digestpara String novamente?
Sajad,
1
@Sajjad Use sua função de codificação base64 favorita. Eu gosto pessoalmente do Apache Commons .
Brendan Long,
1
@Adam Não, isso é enganoso. Chamar ´toString´ em uma matriz de bytes está produzindo um resultado diferente do que converter o conteúdo da matriz de bytes em String, a resposta de Brendan Longs é mais apropriada
max.mustermann
30

Isso já está implementado nas bibliotecas de tempo de execução.

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;
}

Em um ambiente JEE6 +, também é possível usar JAXB DataTypeConverter :

import javax.xml.bind.DatatypeConverter;

String hash = DatatypeConverter.printHexBinary( 
           MessageDigest.getInstance("MD5").digest("SOMESTRING".getBytes("UTF-8")));
empilhador
fonte
3
esta versão tem um bug (pelo menos a partir de hoje e com java8 @ win7). tente hash '1234'. o resultado deve começar com '03ac67 ...' mas na verdade começa com '3ac67 ...'
Chris
@Chris obrigado, consertei isso aqui, mas encontrei uma solução melhor, minha favorita neste momento: stackoverflow.com/questions/415953/…
stacker
1
Para novos leitores deste tópico antigo: O favorito (MD5-hash) referido por @stacker agora é considerado inseguro ( en.wikipedia.org/wiki/MD5 ).
mortensi
1
A condição deve ser output.length () <64, não 32.
Sampo
Como podemos comparar dois valores de hash diferentes criados com o mesmo sal? Suponha que um venha do login do formulário (precisa fazer hash usando Salt antes de comparar) e outro venha do banco de dados e, então, como podemos comparar esses dois?
Pra_A
16

Você não precisa necessariamente da biblioteca BouncyCastle. O código a seguir mostra como fazer isso usando a função 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);
    }
}

Agradecimentos especiais ao user1452273 deste post: Como fazer hash de alguma string com sha256 em Java?

Mantenha o bom trabalho !

Whiplash
fonte
9

Ao usar hashcodes com qualquer provedor jce, você primeiro tenta obter uma instância do algoritmo, depois atualiza-o com os dados que deseja que sejam hash e, quando terminar, chame digest para obter o valor de hash.

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

você pode usar o resumo para obter uma versão codificada em base64 ou hexadecimal de acordo com suas necessidades

Nikolaus Gradwohl
fonte
Por curiosidade, você pode ir direto para digest()a matriz de bytes de entrada, pulando update()?
carregado em
Uma coisa que notei é que isso funciona com "SHA-256", enquanto "SHA256" gera uma NoSuchAlgorithmException. Não é nada demais.
knpwrs
9
De acordo com meu comentário para Brandon, não use String.getBytes()sem especificar uma codificação. Atualmente, esse código pode fornecer resultados diferentes em plataformas diferentes - o que é um comportamento quebrado para um hash bem definido.
Jon Skeet
@ladenedge sim - se sua string for curta o suficiente @KPthunder seu direito @Jon Skeet depende do conteúdo da string - mas sim, adicione uma string de codificação getBytes para estar no lado salvo
Nikolaus Gradwohl
7

Java 8: Base64 disponível:

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

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

Suponho que você esteja usando uma versão relativamente antiga do Java sem SHA-256. Portanto, você deve adicionar o Provedor BouncyCastle aos 'Provedores de Segurança' já fornecidos em sua versão 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
fonte
+1 por mencionar o BouncyCastle, que adiciona alguns novos MessageDigests em comparação com a seleção relativamente insignificante disponível no JDK.
mjuarez
0
return new String(Hex.encode(digest));
Kay
fonte
4
Sem informações de pacote / provedor, a classe "Hex" não é muito útil. E se for Hex do Apache Commons Codec, eu usaria return Hex.encodeHexString(digest).
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
fonte
-1

Isso funcionará com o seguinte pacote "org.bouncycastle.util.encoders.Hex"

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

Está na jarra do bouncycastle.

Shrikant Karvade
fonte