How can I hash a password in Java?

前端 未结 13 2000
暖寄归人
暖寄归人 2020-11-22 05:00

I need to hash passwords for storage in a database. How can I do this in Java?

I was hoping to take the plain text password, add a random salt, then store the salt a

相关标签:
13条回答
  • 2020-11-22 05:50

    Here is a complete implementation with two methods doing exactly what you want:

    String getSaltedHash(String password)
    boolean checkPassword(String password, String stored)
    

    The point is that even if an attacker gets access to both your database and source code, the passwords are still safe.

    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 an attacker.
        private static final int iterations = 20*1000;
        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[] saltAndHash = stored.split("\\$");
            if (saltAndHash.length != 2) {
                throw new IllegalStateException(
                    "The stored password must have the form 'salt$hash'");
            }
            String hashOfInput = hash(password, Base64.decodeBase64(saltAndHash[0]));
            return hashOfInput.equals(saltAndHash[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());
        }
    }
    

    We are storing 'salt$iterated_hash(password, salt)'. The salt are 32 random bytes and it's purpose is that if two different people choose the same password, the stored passwords will still look different.

    The iterated_hash, which is basically hash(hash(hash(... hash(password, salt) ...))) makes it very expensive for a potential attacker who has access to your database to guess passwords, hash them, and look up hashes in the database. You have to compute this iterated_hash every time a user logs in, but it doesn't cost you that much compared to the attacker who spends nearly 100% of their time computing hashes.

    0 讨论(0)
提交回复
热议问题