String Java para SHA1

158

Estou tentando fazer um simples conversor de String para SHA1 em Java e é isso que eu tenho ...

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

Quando passo toSHA1("password".getBytes()), [�a�ɹ??�%l�3~��.sei que provavelmente é uma correção de codificação simples como UTF-8, mas alguém poderia me dizer o que devo fazer para obter o que quero 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Ou estou fazendo isso completamente errado?

Brian
fonte
O algoritmo está SHA1sem o hífen, não sei se isso fará diferença.
The Scrum Meister
É uma boa prática para especificar a codificação de caracteres quando você chamar getBytes(), por exemplo utilizaçãotoSHA1("password".getBytes("UTF-8"))
qwerky
possível duplicata de Java calcular um sha1 de uma string
Tulains Córdova

Respostas:

183

ATUALIZAÇÃO
Você pode usar o Apache Commons Codec (versão 1.7+) para fazer esse trabalho.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Obrigado a Jon Onstott por esta sugestão.


Resposta antiga
Converta seu Byte Array em Hex String. O Real's How To diz como .

return byteArrayToHexString(md.digest(convertme))

e (copiado do 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;
}

BTW, você pode obter uma representação mais compacta usando o Base64. O Apache Commons Codec API 1.4 , possui esse ótimo utilitário para remover toda a dor. consulte aqui

Nishant
fonte
4
base64 e sha1 são muito diferentes - não os sugira como alternativas.
Ryan A.
13
@RyanA .: Pelo que entendi, ele sugere a base64 como uma alternativa à codificação hexadecimal do hash SHA1 (não como uma alternativa ao SHA1 inteiramente).
precisa saber é o seguinte
Ainda não tentei, mas você gostaria de explicar como isso funciona?
Jivay
11
Por que não usar uma biblioteca como, em DigestUtils.sha1Hex("my string")vez de reinventar a roda (embora seja interessante saber como converter em hexadecimal manualmente)?
Jon Onstott
3
Porque quando esta resposta foi escrita, o DigestUtils (1.7 foi lançado em setembro de 2012) não tinha esse recurso. Obrigado por apontar isso. +1
Nishant
67

Esta é a minha solução de converter string para sha1. Funciona bem no meu aplicativo 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
fonte
7
Pode querer especificar que este é java.util.Formatter e precisa de um formatter.close () no final para evitar aviso.
Eric Chen
Shoudl't encryptPassword("test")e echo test|sha1sumno terminal Linux produzir o mesmo resultado? Eles não.
Tulains Córdova
@ TulainsCórdova Sobre a chamada do console: Se você usar echo test, a saída incluindo uma quebra de linha será canalizada para sha1sum. Se você deseja fazer o hash de uma string simples sem quebras de linha à direita, use-o echo -n test | sha1sum. O -nparâmetro faz echoomitir a quebra de linha.
MrSnrub
Menos para a pergunta, mas mais em geral: seus encryptPassword()sons são usados ​​para armazenar dados de autenticação. Observe que sua codificação é vulnerável a ataques de dicionário, pois nenhuma propagação é aplicada. Verifique seu ambiente de segurança se isso é um problema para seu aplicativo!
EagleRainbow 26/01
54

Usando a classe Guava Hashing :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Jan Schaefer
fonte
1
Esta resposta pode precisar de uma atualização, pois isso agora produzirá um aviso de que o Hashing é instável.
Semir Deljić
32

SHA-1 (e todos os outros algoritmos de hash) retornam dados binários. Isso significa que (em Java) eles produzem a byte[]. Esse bytearray não representa nenhum caractere específico, o que significa que você não pode simplesmente transformá-lo Stringcomo você fez.

Se você precisar de um String, precisará formatá-lo byte[]de uma maneira que possa ser representada como um String(caso contrário, apenas mantenha o ambiente byte[]).

Duas maneiras comuns de representar byte[]caracteres arbitrários como imprimíveis são BASE64 ou Hex-Strings simples (ou seja, representando cada um bytepor dois dígitos hexadecimais). Parece que você está tentando produzir uma cadeia hexadecimal.

Há também outra armadilha: se você deseja obter o SHA-1 de um Java String, é necessário convertê-lo Stringem um byte[]primeiro (já que a entrada do SHA-1 também é byte[]). Se você simplesmente usar myString.getBytes()como mostrou, ele usará a codificação padrão da plataforma e, como tal, dependerá do ambiente em que a executar (por exemplo, pode retornar dados diferentes com base na configuração de idioma / localidade do seu sistema operacional).

A melhor solução é especificar a codificação utilizada para a StringPara- byte[]conversão como este: myString.getBytes("UTF-8"). Escolher UTF-8 (ou outra codificação que possa representar todos os caracteres Unicode) é a opção mais segura aqui.

Joachim Sauer
fonte
27

Esta é uma solução simples que pode ser usada ao converter uma string para um 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
fonte
Aviso: a geração de hash está incorreta para os hashes começando com '0'. Você receberá uma String com 39 caracteres.
philn
@philn você poderia sugerir solução?
Nikita Koksharov
1
Eu acho que se você criar um grande número inteiro a partir de um byte [] com zeros à esquerda suficientes, esses 0s serão perdidos. Portanto, a representação da sequência hexadecimal "0" não estará lá, levando a um hash com 39 ou menos caracteres. Eu costumava solução petrnohejls acima e funciona bem ...
philn
25

Basta usar a biblioteca de codecs do apache commons. Eles têm uma classe de utilitário chamada DigestUtils

Não há necessidade de entrar em detalhes.

DaTroop
fonte
51
Eu discordo, entrar em detalhes é meio que o ponto principal
ninesided
12
A questão é se você tem tempo para entrar em detalhes ou não. O ponto principal é geralmente fazê-lo a tempo. Nem todo mundo é aluno ou tem o luxo de aprender todos os detalhes.
DaTroop 13/08/12
DigestUtils retorna uma matriz de bytes, para obter a representação da string que você precisa para executá-la através do Hex.encodeHexString. Java: é 2014 e que ainda não tem um um passo método sha
ryber
5
Método SHA-1 de uma etapa String result = DigestUtils.sha1Hex("An input string")
:;
18

Como mencionado anteriormente, use o codec do apache commons. Também é recomendado pelos caras do Spring (consulte DigestUtils no documento do Spring). Por exemplo:

DigestUtils.sha1Hex(b);

Definitivamente não usaria a resposta mais votada aqui.

kazuar
fonte
7

Não está imprimindo corretamente porque você precisa usar a codificação Base64. Com o Java 8, você pode codificar usando a classe de codificador Base64 .

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

Resultado

Isso fornecerá a saída esperada de 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

Eduardo Dennis
fonte
1
@Devenv é SHA-1, os três pontos significam que ele manterá seu código original que se converte em sha1. O problema original do OP foi ao imprimir a sequência corretamente.
Eduardo Dennis
4

O Resumo da Mensagem (hash) é byte [] in byte [] out

Um resumo da mensagem é definido como uma função que pega uma matriz de bytes brutos e retorna uma matriz de bytes brutos (aka byte[]). Por exemplo, o SHA-1 (algoritmo de hash seguro 1) tem um tamanho de resumo de 160 bits ou 20 bytes. Matrizes de bytes brutos geralmente não podem ser interpretadas como codificações de caracteres como UTF-8 , porque nem todos os bytes em cada ordem são uma codificação legal. Então, convertendo-os em um Stringcom:

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

pode criar algumas seqüências ilegais ou possui indicadores de código para mapeamentos Unicode indefinidos :

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

Codificação de binário para texto

Para essa codificação de binário para texto é usada. Com os hashes, o mais usado é a codificação HEX ou Base16 . Basicamente, um byte pode ter o valor de 0a 255(ou -128a 127assinatura), que é equivalente à representação de HEX 0x00- 0xFF. Portanto, hex dobrará o comprimento necessário da saída, o que significa que uma saída de 20 bytes criará uma cadeia hexadecimal de 40 caracteres, por exemplo:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Observe que não é necessário usar a codificação hexadecimal. Você também pode usar algo como base64 . O hex é frequentemente preferido porque é mais fácil de ser lido por humanos e tem um comprimento de saída definido sem a necessidade de preenchimento.

Você pode converter uma matriz de bytes em hexadecimal apenas com a funcionalidade JDK:

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

Observe, no entanto, que BigIntegerinterpretará a matriz de bytes fornecida como número e não como uma sequência de bytes. Isso significa que os zeros à esquerda não serão gerados e a sequência resultante poderá ser menor que 40 caracteres.

Usando bibliotecas para codificar para HEX

Agora você pode copiar e colar um método não testado de bytes para hexa do Stack Overflow ou usar dependências massivas como o Guava .

Para ter uma solução básica para a maioria dos problemas relacionados a bytes, implementei um utilitário para lidar com estes casos: bytes-java (Github)

Para converter sua matriz de bytes de resumo da mensagem, você pode simplesmente fazer

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

ou você pode simplesmente usar o recurso de hash embutido

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

Representação da Base 64 do SHA1Hash:

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

A razão pela qual isso não funciona é que, quando você liga String(md.digest(convertme)), está dizendo ao Java para interpretar uma sequência de bytes criptografados como uma String. O que você deseja é converter os bytes em caracteres hexadecimais.

Zarkonnen
fonte
0

Converter matriz de bytes em sequência 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
fonte