Creating the same hashes like symfony2 using java (SHA-512)

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);

  // "stretch" hash
  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 {
  // Get digester instance for algorithm "SHA-512" using BounceCastle
  MessageDigest digester = MessageDigest.getInstance("SHA-512", new BouncyCastleProvider());

  // Create salted password string
  String mergedPasswordAndSalt = clearPassword + "{" + salt + "}";

  // First time hash the input string by using UTF-8 encoded bytes.
  byte[] hash = digester.digest(mergedPasswordAndSalt.getBytes("UTF-8"));

  // Loop 5k times
  for (int i = 0; i < 5000; i++) {
    // Concatenate the hash bytes with the clearPassword bytes and rehash
    hash = digester.digest(ArrayUtils.addAll(hash, mergedPasswordAndSalt.getBytes("UTF-8")));
  }

  // Log the resulting hash as base64 String
  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

+4
1

php 5k , , 4999 .

$digest = hash($this->algorithm, $salted, true);

for ($i = 1; $i < $this->iterations; ++$i) {
  $digest = hash($this->algorithm, $digest.$salted, true);
}

java for 0, 5k + 1.

for 1 java, .

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")));
}
+3

Source: https://habr.com/ru/post/1652171/


All Articles