-
-
Save lukaszb/1af1bd4233326e37a8a0 to your computer and use it in GitHub Desktop.
/* Example implementation of password hasher similar to Django's PasswordHasher | |
* Requires Java8 (but should be easy to port to older JREs) | |
* Currently it would work only for pbkdf2_sha256 algorithm | |
* | |
* Django code: https://github.com/django/django/blob/1.6.5/django/contrib/auth/hashers.py#L221 | |
*/ | |
import java.nio.charset.Charset; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.spec.InvalidKeySpecException; | |
import java.security.spec.KeySpec; | |
import java.util.Arrays; | |
import java.util.Base64; | |
import javax.crypto.SecretKey; | |
import javax.crypto.SecretKeyFactory; | |
import javax.crypto.spec.PBEKeySpec; | |
class Hasher { | |
public final Integer DEFAULT_ITERATIONS = 10000; | |
public final String algorithm = "pbkdf2_sha256"; | |
public Hasher() {} | |
public String getEncodedHash(String password, String salt, int iterations) { | |
// Returns only the last part of whole encoded password | |
SecretKeyFactory keyFactory = null; | |
try { | |
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); | |
} catch (NoSuchAlgorithmException e) { | |
System.err.println("Could NOT retrieve PBKDF2WithHmacSHA256 algorithm"); | |
System.exit(1); | |
} | |
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes(Charset.forName("UTF-8")), iterations, 256); | |
SecretKey secret = null; | |
try { | |
secret = keyFactory.generateSecret(keySpec); | |
} catch (InvalidKeySpecException e) { | |
System.out.println("Could NOT generate secret key"); | |
e.printStackTrace(); | |
} | |
byte[] rawHash = secret.getEncoded(); | |
byte[] hashBase64 = Base64.getEncoder().encode(rawHash); | |
return new String(hashBase64); | |
} | |
public String encode(String password, String salt, int iterations) { | |
// returns hashed password, along with algorithm, number of iterations and salt | |
String hash = getEncodedHash(password, salt, iterations); | |
return String.format("%s$%d$%s$%s", algorithm, iterations, salt, hash); | |
} | |
public String encode(String password, String salt) { | |
return this.encode(password, salt, this.DEFAULT_ITERATIONS); | |
} | |
public boolean checkPassword(String password, String hashedPassword) { | |
// hashedPassword consist of: ALGORITHM, ITERATIONS_NUMBER, SALT and | |
// HASH; parts are joined with dollar character ("$") | |
String[] parts = hashedPassword.split("\\$"); | |
if (parts.length != 4) { | |
// wrong hash format | |
return false; | |
} | |
Integer iterations = Integer.parseInt(parts[1]); | |
String salt = parts[2]; | |
String hash = encode(password, salt, iterations); | |
return hash.equals(hashedPassword); | |
} | |
// Following examples can be generated at any Django project: | |
// | |
// >>> from django.contrib.auth.hashers import make_password | |
// >>> make_password('mystery', hasher='pbkdf2_sha256') # salt would be randomly generated | |
// 'pbkdf2_sha256$10000$HqxvKtloKLwx$HdmdWrgv5NEuaM4S6uMvj8/s+5Yj+I/d1ay6zQyHxdg=' | |
// >>> make_password('mystery', salt='mysalt', hasher='pbkdf2_sha256') | |
// 'pbkdf2_sha256$10000$mysalt$KjUU5KrwyUbKTGYkHqBo1IwUbFBzKXrGQgwA1p2AuY0=' | |
// | |
// | |
// mystery | |
// pbkdf2_sha256$10000$qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ= | |
// | |
// s3cr3t | |
// pbkdf2_sha256$10000$BjDHOELBk7fR$xkh1Xf6ooTqwkflS3rAiz5Z4qOV1Jd5Lwd8P+xGtW+I= | |
// | |
// puzzle | |
// pbkdf2_sha256$10000$IFYFG7hiiKYP$rf8vHYFD7K4q2N3DQYfgvkiqpFPGCTYn6ZoenLE3jLc= | |
// | |
// riddle | |
// pbkdf2_sha256$10000$A0S5o3pNIEq4$Rk2sxXr8bonIDOGj6SU4H/xpjKHhHAKpFXfmNZ0dnEY= | |
public static void main(String[] args) { | |
runTests(); | |
} | |
private static void runTests() { | |
System.out.println("==========================="); | |
System.out.println("= Testing password hasher ="); | |
System.out.println("==========================="); | |
System.out.println(); | |
System.out.println(); | |
passwordShouldMatch("mystery", "pbkdf2_sha256$10000$qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ="); | |
passwordShouldMatch("mystery", "pbkdf2_sha256$10000$mysalt$KjUU5KrwyUbKTGYkHqBo1IwUbFBzKXrGQgwA1p2AuY0="); // custom salt | |
passwordShouldMatch("s3cr3t", "pbkdf2_sha256$10000$BjDHOELBk7fR$xkh1Xf6ooTqwkflS3rAiz5Z4qOV1Jd5Lwd8P+xGtW+I="); | |
passwordShouldMatch("puzzle", "pbkdf2_sha256$10000$IFYFG7hiiKYP$rf8vHYFD7K4q2N3DQYfgvkiqpFPGCTYn6ZoenLE3jLc="); | |
passwordShouldMatch("riddle", "pbkdf2_sha256$10000$A0S5o3pNIEq4$Rk2sxXr8bonIDOGj6SU4H/xpjKHhHAKpFXfmNZ0dnEY="); | |
System.out.println(); | |
passwordShouldNotMatch("foo", ""); | |
passwordShouldNotMatch("mystery", "pbkdf2_md5$10000$qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ="); | |
passwordShouldNotMatch("mystery", "pbkdf2_sha1$10000$qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ="); | |
passwordShouldNotMatch("mystery", "pbkdf2_sha256$10001$Qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ="); | |
passwordShouldNotMatch("mystery", "pbkdf2_sha256$10001$qx1ec0f4lu4l$3G81rAm/4ng0tCCPTrx2aWohq7ztDBfFYczGNoUtiKQ="); | |
passwordShouldNotMatch("mystery", "pbkdf2_sha256$10000$qx7ztDBfFYczGNoUtiKQ="); | |
passwordShouldNotMatch("s3cr3t", "pbkdf2_sha256$10000$BjDHOELBk7fR$foobar"); | |
passwordShouldNotMatch("puzzle", "pbkdf2_sha256$10000$IFYFG7hiiKYP$rf8vHYFD7K4q2N3DQYfgvkiqpFPGCTYn6ZoenLE3jLcX"); | |
} | |
private static void passwordShouldMatch(String password, String expectedHash) { | |
Hasher hasher = new Hasher(); | |
if (hasher.checkPassword(password, expectedHash)) { | |
System.out.println(" => OK"); | |
} else { | |
String[] parts = expectedHash.split("\\$"); | |
if (parts.length != 4) { | |
System.out.printf(" => Wrong hash provided: '%s'\n", expectedHash); | |
return; | |
} | |
String salt = parts[2]; | |
String resultHash = hasher.encode(password, salt); | |
String msg = " => Wrong! Password '%s' hash expected to be '%s' but is '%s'\n"; | |
System.out.printf(msg, password, expectedHash, resultHash); | |
} | |
} | |
private static void passwordShouldNotMatch(String password, String expectedHash) { | |
Hasher hasher = new Hasher(); | |
if (hasher.checkPassword(password, expectedHash)) { | |
System.out.printf(" => Wrong (password '%s' did '%s' match but were not supposed to)\n", password, expectedHash); | |
} else { | |
System.out.println(" => OK (password didn't match)"); | |
} | |
} | |
} |
Thanks! This is exactly what I want.
Thanks! This is the last bit I needed to finish migrating from Django to Java 👍
This code not working to migrate to JAVA from Django users and pass, the hashes not match.
Thanks!
Thanks
Precisely what I needed! This is great, thank you.
Thanks!
Thanks! Helped me a lot!
thanks
Thank you kind sir!
Thanks!
Thank you!
I used this a couple years ago to address a performance problem in a legacy python app to check the incoming requests using the python generated api key. I'm now completely rewriting that python app in java and I need a way to generate the key as well. I'm having trouble getting the salt, prefix and password created. Would it be possible to update this example with something that creates a prefix, salt etc.
thanks!
nvm, I figured it out. it was a stupid mistake lol
Hi Łukasz,
here is version working with JDK 1.7. As there is no implementation of PBKDF2WithHmacSHA256 in Java 7 it must use BouncyCastle library.