Voici une mise en œuvre complète avec deux méthodes pour faire exactement ce que vous voulez:
String getSaltedHash(String password)
boolean checkPassword(String password, String stored)
Le point est que, même si un attaquant obtient l'accès à votre base de données et le code source, les mots de passe sont toujours en sécurité.
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;
public class Password {
// The higher the number of iterations the more
// expensive computing the hash is for us
// and also for a brute force attack.
private static final int iterations = 10*1024;
private static final int saltLen = 32;
private static final int desiredKeyLen = 256;
/** Computes a salted PBKDF2 hash of given plaintext password
suitable for storing in a database.
Empty passwords are not supported. */
public static String getSaltedHash(String password) throws Exception {
byte[] salt = SecureRandom.getInstance("SHA1PRNG").generateSeed(saltLen);
// store the salt with the password
return Base64.encodeBase64String(salt) + "$" + hash(password, salt);
}
/** Checks whether given plaintext password corresponds
to a stored salted hash of the password. */
public static boolean check(String password, String stored) throws Exception{
String[] saltAndPass = stored.split("\\$");
if (saltAndPass.length != 2)
return false;
String hashOfInput = hash(password, Base64.decodeBase64(saltAndPass[0]));
return hashOfInput.equals(saltAndPass[1]);
}
// using PBKDF2 from Sun, an alternative is https://github.com/wg/scrypt
// cf. http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html
private static String hash(String password, byte[] salt) throws Exception {
if (password == null || password.length() == 0)
throw new IllegalArgumentException("Empty passwords are not supported.");
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey key = f.generateSecret(new PBEKeySpec(
password.toCharArray(), salt, iterations, desiredKeyLen)
);
return Base64.encodeBase64String(key.getEncoded());
}
}
Nous sommes stocker 'salt$iterated_hash(password, salt)'
. Le sel de 32 octets aléatoires, dont le but est que si deux personnes différentes à choisir le même mot de passe, les mots de passe stockés toujours différents. L' iterated_hash
, ce qui est fondamentalement hash(hash(hash(... hash(password, salt) ...)))
le rend très cher pour un attaquant potentiel qui a accès à votre base de données pour deviner les mots de passe, de hachage, et de rechercher des hachages dans la base de données. Vous avez pour le calcul de ce iterated_hash
chaque fois qu'un utilisateur se connecte, mais il ne vous coûtera pas beaucoup par rapport à l'attaquant qui passe près de 100% de leur temps à calculer les hachages.