Chaîne Java à SHA1

158

J'essaie de créer un simple convertisseur String en SHA1 en Java et c'est ce que j'ai ...

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

Quand je le passe toSHA1("password".getBytes()), je [�a�ɹ??�%l�3~��.sais que c'est probablement un simple correctif d'encodage comme UTF-8, mais quelqu'un pourrait-il me dire ce que je dois faire pour obtenir ce que je veux 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Ou est-ce que je fais cela complètement mal?

Brian
la source
L'algorithme est SHA1sans trait d'union, je ne sais pas si cela fera une différence.
The Scrum Meister le
Il est getBytes()toSHA1("password".getBytes("UTF-8"))
recommandé
duplication possible de Java calculer un sha1 d'une chaîne
Tulains Córdova

Réponses:

183

MISE À JOUR
Vous pouvez utiliser Apache Commons Codec (version 1.7+) pour faire ce travail pour vous.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Merci à @ Jon Onstott pour cette suggestion.


Ancienne réponse
Convertissez votre tableau d'octets en chaîne hexadécimale. Real's How To vous explique comment .

return byteArrayToHexString(md.digest(convertme))

et (copié 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;
}

BTW, vous pouvez obtenir une représentation plus compacte en utilisant Base64. Apache Commons Codec API 1.4 , a cet utilitaire sympa pour éliminer toute la douleur. référez-vous ici

Nishant
la source
4
base64 et sha1 sont très différents - ne les suggérez pas comme alternatives.
Ryan A.
13
@RyanA .: Si je comprends bien, il suggère la base64 comme alternative au codage hexadécimal du hachage SHA1 (pas comme une alternative à SHA1 entièrement).
helmbert
Je ne l'ai pas encore essayé, mais voudriez-vous expliquer comment cela fonctionne?
Jivay
11
Pourquoi ne pas utiliser une bibliothèque comme DigestUtils.sha1Hex("my string")au lieu de réinventer la roue (même si c'est intéressant de savoir comment convertir en hexagone à la main)?
Jon Onstott
3
Parce que lorsque cette réponse a été écrite, DigestUtils (1.7 est sorti en septembre 2012) n'avait pas cette fonctionnalité. Merci de l'avoir signalé. +1
Nishant
67

Ceci est ma solution de conversion de chaîne en sha1. Cela fonctionne bien dans mon application 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
la source
7
Vous voudrez peut-être spécifier qu'il s'agit de java.util.Formatter et a besoin d'un formatter.close () à la fin pour éviter un avertissement.
Eric Chen
Ne devrait-on pas encryptPassword("test")et echo test|sha1sumdans le terminal Linux produire le même résultat? Ils ne le font pas.
Tulains Córdova
@ TulainsCórdova Concernant l'invocation de la console: Si vous utilisez echo test, la sortie comprenant un saut de ligne sera redirigée vers sha1sum. Si vous souhaitez hacher une chaîne simple sans saut de ligne de fin, vous pouvez utiliser echo -n test | sha1sum. Le -nparamètre fait echoomettre le saut de ligne.
MrSnrub
Moins à la question, mais plus en général: vos encryptPassword()sons ressemblent à ceux utilisés pour stocker les données d'authentification. Notez que votre codage est vulnérable aux attaques par dictionnaire, car aucun amorçage n'est appliqué. Vérifiez votre environnement de sécurité si cela pose un problème pour votre application!
EagleRainbow le
54

Utilisation de la classe de hachage Guava :

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Jan Schaefer
la source
1
Cette réponse peut nécessiter une mise à jour car cela produira désormais un avertissement indiquant que le hachage est instable.
Semir Deljić
32

SHA-1 (et tous les autres algorithmes de hachage) renvoient des données binaires. Cela signifie que (en Java) ils produisent un fichier byte[]. Ce bytetableau ne pas représenter tous les caractères spécifiques, ce qui signifie que vous ne pouvez pas tourner simplement en un Stringcomme vous l'avez fait.

Si vous avez besoin d'un String, vous devez le formater byte[]d'une manière qui peut être représentée par un String(sinon, gardez simplement le byte[]autour).

Deux manières courantes de représenter des byte[]caractères arbitraires sous forme de caractères imprimables sont BASE64 ou de simples chaînes hexadécimales (c'est-à-dire représentant chacune bytepar deux chiffres hexadécimaux). Il semble que vous essayez de produire une chaîne hexadécimale.

Il y a aussi un autre piège: si vous voulez obtenir le SHA-1 d'un Java String, vous devez le convertir Stringen un byte[]premier (car l'entrée de SHA-1 est également un byte[]). Si vous utilisez simplement myString.getBytes()comme vous l'avez montré, alors il utilisera le codage par défaut de la plate-forme et en tant que tel dépendra de l'environnement dans lequel vous l'exécutez (par exemple, il pourrait renvoyer des données différentes en fonction du paramètre de langue / locale de votre système d'exploitation).

Une meilleure solution est de spécifier le codage à utiliser pour la String-à- byte[]conversion comme ceci: myString.getBytes("UTF-8"). Choisir UTF-8 (ou un autre encodage qui peut représenter chaque caractère Unicode) est le choix le plus sûr ici.

Joachim Sauer
la source
27

Il s'agit d'une solution simple qui peut être utilisée lors de la conversion d'une chaîne au format hexadécimal:

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
la source
Avertissement: la génération de hachage est incorrecte pour les hachages commençant par «0». Vous recevrez une chaîne de 39 caractères.
philn
@philn pourriez-vous suggérer une solution?
Nikita Koksharov
1
Je suppose que si vous créez un grand entier à partir d'un octet [] avec suffisamment de zéros non significatifs, ces 0 sont perdus. Ainsi, la représentation de chaîne hexadécimale "0" ne sera pas là, conduisant à un hachage avec 39 caractères ou même moins. J'ai utilisé la solution petrnohejls ci-dessus et cela fonctionne très bien ...
philn
25

Utilisez simplement la bibliothèque de codecs apache commons. Ils ont une classe utilitaire appelée DigestUtils

Pas besoin d'entrer dans les détails.

DaTroop
la source
51
Je ne suis pas d'accord, entrer dans les détails est en quelque sorte l'
essentiel
12
La question est de savoir si vous avez le temps d'entrer dans les détails ou non. Le but est généralement de le faire à temps. Tout le monde n'est pas étudiant ou n'a pas le luxe d'apprendre tous les détails.
DaTroop
DigestUtils renvoie un tableau d'octets pour obtenir la représentation sous forme de chaîne dont vous avez besoin pour l'exécuter via Hex.encodeHexString. Java: nous sommes en 2014 et nous n'avons toujours pas de méthode sha en une étape
Ryber
5
Méthode SHA-1 en une étape:; String result = DigestUtils.sha1Hex("An input string")o)
Jon Onstott
18

Comme mentionné précédemment, utilisez le codec apache commons. Il est également recommandé par les gars de Spring (voir DigestUtils dans Spring doc). Par exemple:

DigestUtils.sha1Hex(b);

N'utiliserait certainement pas la réponse la mieux notée ici.

Kazuar
la source
7

Il ne s'imprime pas correctement car vous devez utiliser le codage Base64. Avec Java 8, vous pouvez encoder à l'aide de la classe d'encodeur Base64 .

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

Résultat

Cela vous donnera la sortie attendue de 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

Eduardo Dennis
la source
1
@Devenv c'est SHA-1 les trois points signifient qu'il conservera son code d'origine qui se convertit en sha1. Le problème initial d'OP était lors de l'impression correcte de la chaîne.
Eduardo Dennis
4

Message Digest (hash) est l'octet [] dans l'octet [] sortant

Un résumé de message est défini comme une fonction qui prend un tableau d'octets bruts et renvoie un tableau d'octets bruts (aka byte[]). Par exemple, SHA-1 (Secure Hash Algorithm 1) a une taille de résumé de 160 bits ou 20 octets. Les tableaux d'octets bruts ne peuvent généralement pas être interprétés comme des encodages de caractères comme UTF-8 , car chaque octet de chaque ordre n'est pas un encodage légal. Donc, les convertir en un Stringavec:

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

peut créer des séquences illégales ou avoir des pointeurs de code vers des mappages Unicode non définis :

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

Encodage binaire en texte

Pour cela, l' encodage binaire en texte est utilisé. Avec les hachages, celui qui est le plus utilisé est l' encodage HEX ou Base16 . Fondamentalement, un octet peut avoir la valeur de 0to 255(ou -128to 127signed) qui est équivalente à la représentation HEX de 0x00- 0xFF. Par conséquent, hex doublera la longueur requise de la sortie, ce qui signifie qu'une sortie de 20 octets créera une chaîne hexadécimale de 40 caractères, par exemple:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Notez qu'il n'est pas nécessaire d'utiliser le codage hexadécimal. Vous pouvez également utiliser quelque chose comme base64 . Hex est souvent préféré car il est plus facile à lire par les humains et a une longueur de sortie définie sans avoir besoin de remplissage.

Vous pouvez convertir un tableau d'octets en hexadécimal avec la seule fonctionnalité JDK:

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

Notez cependant que BigIntegercela interprétera le tableau d'octets donné comme un nombre et non comme une chaîne d'octets. Cela signifie que les zéros non significatifs ne seront pas affichés et que la chaîne résultante peut être inférieure à 40 caractères.

Utilisation de bibliothèques pour encoder en HEX

Vous pouvez maintenant copier et coller une méthode octet-hexadécimal non testée à partir de Stack Overflow ou utiliser des dépendances massives comme Guava .

Pour avoir une solution idéale pour la plupart des problèmes liés aux octets, j'ai implémenté un utilitaire pour gérer ces cas: bytes-java (Github)

Pour convertir votre tableau d'octets de résumé de message, vous pouvez simplement faire

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

ou vous pouvez simplement utiliser la fonction de hachage intégrée

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

Représentation en base 64 de SHA1Hash:

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

La raison pour laquelle cela ne fonctionne pas est que lorsque vous appelez String(md.digest(convertme)), vous dites à Java d'interpréter une séquence d'octets chiffrés comme une chaîne. Ce que vous voulez, c'est convertir les octets en caractères hexadécimaux.

Zarkonnen
la source
0

Convertit le tableau d'octets en chaîne hexadécimale.

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
la source