...
Code Block | ||
---|---|---|
| ||
import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.security.spec.KeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; final class Password { private SecureRandom random = new SecureRandom(); private final int SALT_BYTE_LENGTH = 12; private final int ITERATIONS = 100000; private final int KEY_BIT_LENGTH = 128; private final String ALGORITHM = "PBKDF2WithHmacSHA256"; /* Set password to new value, zeroing out password */ void setPassword(char[] pass) throws IOException, GeneralSecurityException { byte[] salt = new byte[12SALT_BYTE_LENGTH]; random.nextBytes(salt); saveBytes(salt, "salt.bin"); byte[] hashVal = hashPassword( pass, salt); saveBytes(hashVal,"password.bin"); Arrays.fill(hashVal, (byte) 0); } /* Indicates if given password is correct */ boolean checkPassword(char[] pass) throws IOException, GeneralSecurityException { byte[] salt = loadBytes("salt.bin"); byte[] hashVal1 = hashPassword( pass, salt); // Load the hash value stored in password.bin byte[] hashVal2 = loadBytes("password.bin"); boolean arraysEqual = timingEquals( hashVal1, hashVal2); Arrays.fill(hashVal1, (byte) 0); Arrays.fill(hashVal2, (byte) 0); return arraysEqual; } /* Encrypts password & salt and zeroes both */ private byte[] hashPassword(char[] pass, byte[] salt) throws GeneralSecurityException { KeySpec spec = new PBEKeySpec(pass, salt, 65536ITERATIONS, 128KEY_BIT_LENGTH); Arrays.fill(pass, (char) 0); Arrays.fill(salt, (byte) 0); SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"ALGORITHM); return f.generateSecret(spec).getEncoded(); }} /** * Indicates if both byte arrays are equal * but uses same amount of time if they are the same or different * to prevent timing attacks */ public static boolean timingEquals(byte b1[], byte b2[]) { boolean result = true; int len = b1.length; if (len != b2.length) { result = false; } if (len > b2.length) { len = b2.length; } for (int i = 0; i < len; i++) { result &= (b1[i] == b2[i]); } return result; } private void saveBytes(byte[] bytes, String filename) throws IOException { // ... write bytes to the file } private byte[] loadBytes(String filename) throws IOException { // ... read bytes to the file } } |
...
Finally, it uses PBKDF2 which, unlike MessageDigest
, is specifically designed for hashing passwords.
The parametric values (SALT_BYTE_LENGTH, ITERATIONS, KEY_BIT_LENGTH, ALGORITHM) should be set to values that reflect current best practices. It should also be noted that once these parametric values are set they can not be changed without having to re-hash all passwords with the new parametric values.
Applicability
Passwords stored without a secure hash are exposed to malicious users. Violations of this guideline generally have a clear exploit associated with them.
...