I have an old Symfony2 based application and am developing a Dropwizard replacement for Java. I transferred all user records from the old database to the new Datamodel. I also added new password fields and imported old password and salt fields.
Now I want to make a well-known procedure. Let the user log in, try the new password field. If this fails, try migrated ones, if they work, encode the cleartext password with the new algorithm and save the new hash in the new pasword field. So users transfer password hashes from the old procedure to the new one.
It sounds simple and normal, it works as usual, but this Symfony and PHP are driving me crazy.
Where I am stuck, I need to create the same hash with java as symfony. The old application uses MessageDigestPasswordEncoder encoded with "sha512", base64 and 5000 iterations, all default values;)
Important methods are:
MessageDigestPasswordEncoder:
public function encodePassword($raw, $salt) {
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
if (!in_array($this->algorithm, hash_algos(), true)) {
throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
}
$salted = $this->mergePasswordAndSalt($raw, $salt);
$digest = hash($this->algorithm, $salted, true);
for ($i = 1; $i < $this->iterations; ++$i) {
$digest = hash($this->algorithm, $digest.$salted, true);
}
return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
}
And BasePasswordEncoder:
protected function mergePasswordAndSalt($password, $salt) {
if (empty($salt)) {
return $password;
}
if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) {
throw new \InvalidArgumentException('Cannot use { or } in salt.');
}
return $password.'{'.$salt.'}';
}
It seems straightforward, but I stuck to that. As I read this, he does:
- Combine salt and clear text for: "password {salt}"
- Hash this line with SHA-512 and return the binary string to the digest variable
- iterate 5k times and use the digest combined with the combined plaintext password to rephrase in the digest
- encode base64 digest
So here is my attempt in Java:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public void legacyEncryption(String salt, String clearPassword) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest digester = MessageDigest.getInstance("SHA-512", new BouncyCastleProvider());
String mergedPasswordAndSalt = clearPassword + "{" + salt + "}";
byte[] hash = digester.digest(mergedPasswordAndSalt.getBytes("UTF-8"));
for (int i = 0; i < 5000; i++) {
hash = digester.digest(ArrayUtils.addAll(hash, mergedPasswordAndSalt.getBytes("UTF-8")));
}
logger.info("Legace password digest: salt=" + salt + " hash=" + Base64.getEncoder().encodeToString(hash));
}
Does anyone see a problem? I think the difference is this: PHP: binary.binary and JAVA: addAll (byte [], byte [])
Thanks in advance