Je stocke le mot de passe de l'utilisateur sur la base de données sous forme de hachage sha1.
Malheureusement, je reçois des réponses étranges.
Je stocke la chaîne comme ceci:
MessageDigest cript = MessageDigest.getInstance("SHA-1");
cript.reset();
cript.update(userPass.getBytes("utf8"));
this.password = new String(cript.digest());
Je voulais quelque chose comme ça ->
aff -> "0c05aa56405c447e6678b7f3127febde5c3a9238"
plutôt que
aff -> �V @\D ~ fx����: 8
Cela se produit car cript.digest () renvoie un tableau d'octets que vous essayez d'imprimer sous forme de chaîne de caractères. Vous voulez le convertir en une chaîne hexagonale imprimable.
Solution facile: Utilisez Apache librairie commons-codec :
String password = new String(Hex.encodeHex(cript.digest()),
CharSet.forName("UTF-8"));
Utilisation de la bibliothèque de codecs commune Apache:
DigestUtils.sha1Hex("aff")
Le résultat est 0c05aa56405c447e6678b7f3127febde5c3a9238
C'est ça :)
Une itération d'un algorithme de hachage n'est pas sécurisée. C'est trop rapide. Vous devez renforcer les clés en répétant plusieurs fois le hachage.
De plus, vous ne salez pas le mot de passe. Cela crée une vulnérabilité aux dictionnaires pré-calculés, tels que "Tables Rainbow".
Au lieu d’essayer de faire rouler votre propre code (ou d’utiliser un bloatware tiers peu précis) pour le faire correctement, vous pouvez utiliser le code intégré à la Java runtime. Voir this répondre pour plus de détails.
Une fois que vous avez haché le mot de passe correctement, vous obtenez un byte[]
. Un moyen facile de convertir cela en hexadécimal String
est avec la classe BigInteger
:
String passwordHash = new BigInteger(1, cript.digest()).toString(16);
Si vous voulez vous assurer que votre chaîne a toujours 40 caractères, vous devrez peut-être ajouter des zéros à gauche (vous pouvez le faire avec String.format()
.).
Si vous ne souhaitez pas ajouter de dépendances supplémentaires à votre projet, vous pouvez également utiliser
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);
La méthode crypt.digest () renvoie un octet []. Ce tableau d'octets est la somme SHA-1 correcte, mais les hachages de chiffrement sont généralement affichés aux humains sous forme hexadécimale. Chaque octet dans votre hachage donnera deux chiffres hexadécimaux.
Pour convertir en toute sécurité un octet en hex, utilisez ceci:
// %1$ == arg 1
// 02 == pad with 0's
// x == convert to hex
String hex = String.format("%1$02x", byteValue);
Cet extrait de code peut être utilisé pour convertir un caractère en hexadécimal :
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Java.io.*;
public class UnicodeFormatter {
static public String byteToHex(byte b) {
// Returns hex String representation of byte b
char hexDigit[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
return new String(array);
}
static public String charToHex(char c) {
// Returns hex String representation of char c
byte hi = (byte) (c >>> 8);
byte lo = (byte) (c & 0xff);
return byteToHex(hi) + byteToHex(lo);
}
}
Notez que travailler avec des octets dans Java est très sujet aux erreurs. Je revérifierais tout et testerais également des cas étranges.
Aussi, vous devriez envisager d'utiliser quelque chose de plus fort que SHA-1. http://csrc.nist.gov/groups/ST/hash/statement.html
Avec Google Guava :
Maven:
<dependency>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
<version>14.0.1</version>
</dependency>
Échantillon:
HashCode hashCode = Hashing.sha1().newHasher()
.putString(password, Charsets.UTF_8)
.hash();
String hash = BaseEncoding.base16().lowerCase().encode(hashCode.asBytes());
Si vous utilisez Spring c'est assez simple:
MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");
Le stockage de mots de passe non réversibles ne se limite pas à de simples algorithmes de hachage standard.
Pour plus d'informations, voir par exemple.
Vous pouvez également utiliser une méthode http://en.wikipedia.org/wiki/Password-authenticated_key_agreement pour éviter de transmettre le mot de passe en texte clair au serveur.
Que diriez-vous de convertir byte [] en chaîne base64?
byte[] chkSumBytArr = digest.digest();
BASE64Encoder encoder = new BASE64Encoder();
String base64CheckSum = encoder.encode(chkSumBytArr);
Pour utiliser UTF-8, procédez comme suit:
userPass.getBytes("UTF-8");
Et pour obtenir une chaîne Base64 du résumé, vous pouvez faire quelque chose comme ceci:
this.password = new BASE64Encoder().encode(cript.digest());
Puisque MessageDigest.digest()
renvoie un tableau d'octets, vous pouvez le convertir en chaîne à l'aide d'Apache Hex Encoding (plus simple).
Par exemple.
this.password = Hex.encodeHexString(cript.digest());
digest () renvoie un tableau d'octets que vous convertissez en chaîne à l'aide de l'encodage par défaut. Ce que vous voulez faire, c'est l'encoder en base64.
echo -n "aff" | sha1sum produit le bon résultat (echo insère une nouvelle ligne par défaut)
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.reset();
messageDigest.update(password.getBytes("UTF-8"));
String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);
vous pouvez aussi utiliser ce code (à partir de crackstation.net):
private static String toHex(byte[] array) { BigInteger bi = new BigInteger(1, array); String hex = bi.toString(16); int paddingLength = (array.length * 2) - hex.length(); if(paddingLength > 0) return String.format("%0" + paddingLength + "d", 0) + hex; else return hex; }
Vous devez d'abord encoder le résultat. MessageDigest
renvoie un hachage "brut", plutôt qu'un lisible par l'homme.
Modifier:
@thejh a fourni un lien vers le code qui devrait fonctionner. Personnellement, je suggérerais d'utiliser soit Bouncycastle ou Apache Commons Codec pour faire le travail. Bouncycastle serait utile si vous souhaitez effectuer d’autres opérations liées à la cryptographie.