Since hashing algorithms isn't something to mess around with as a beginner. Several howto's explain that one should use already made solutions. Examples of such pages:
- https://crackstation.net/hashing-security.htm
- https://owasp.org/www-project-cheat-sheets/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
So I opted for ready-made solutions from java.security
and java.crypto
library packages.
But I feel there is still room for messing up, especially with regard to the parameters in PBEKeySpec(password, salt, iterations, keyLength)
.
According to some HOWTOs, salt should be as long in bytes as the output from the hash function. So I chose 64, since SHA-512 outputs (64*8=512 bits) 64 bytes. (Although, it seems I can get larger output simply by increasing keyLength
argument above 512, which makes me wonder).
So basically. Do you guys/girls find this code reasonable? Especially in regards to the arguments passed? If not, please tell me what to change, how and why.
package my.application;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.apache.commons.codec.binary.Hex;
public class PasswordHasher {
private int ammountOfBytes = 64;
private int keyLength = 512;
private int iterations = 100000;
private byte[] salt;
private byte[] hash;
private char[] password;
private String saltHex;
private String hashHex;
public void hash(String rawPassword) {
makeSalt();
password = rawPassword.toCharArray();
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
SecretKey key;
try {
SecretKeyFactory generator = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
key = generator.generateSecret(spec);
hash = key.getEncoded();
hashHex = Hex.encodeHexString(hash);
spec.clearPassword();
key = null;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
public void destroy() {
saltHex = null;
hashHex = null;
Arrays.fill(password, (char) 0);
Arrays.fill(salt, (byte) 0);
Arrays.fill(hash, (byte) 0);
}
private String makeSalt() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
salt = new byte[ammountOfBytes];
random.nextBytes(salt);
saltHex = Hex.encodeHexString(salt);
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return saltHex;
}
public String getSaltHex() {
return saltHex;
}
public String getHashHex() {
return hashHex;
}
}