像symfony2一样创建相同的哈希值(java-SHA-512)

时间:2016-08-23 10:14:33

标签: java php symfony hash sha

我在这里有一个旧的基于Symfony2的应用程序,我正在使用Java中的Dropwizard开发替代品。 我将旧DB中的所有用户记录迁移到我的新Datamodel中。 我还为密码添加了新字段,并导入了旧密码和盐字段。

现在我想做出众所周知的程序。让用户登录,尝试新密码字段。如果失败,请尝试迁移的,如果它们有效,则使用新算法对明文密码进行编码,并将新散列存储在新的密码字段中。这样用户就可以将旧程序中的密码哈希移植到新程序中。

听起来简单而且规范,它像往常一样工作,但这个Symfony和PHP让我发疯。

我坚持的地方是用symfony创建与java相同的哈希。 旧应用程序使用带有“sha512”,base64编码和5000次迭代的MessageDigestPasswordEncoder,所有默认值;)

重要的方法是:

MessageDigestPasswordEncoder:

$this-getMockForAbstractClass('FOS\UserBundle\Model\User');

和BasePasswordEncoder:

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

这似乎是直截了当但我坚持下去。 正如我读到的那样:

  1. 将salt和明文密码合并为:“password {salt}”
  2. 使用SHA-512散列此字符串并将二进制字符串返回到摘要变量
  3. 迭代5k次并使用与合并的明文密码连接的摘要重新进入摘要
  4. 将摘要编码为base64
  5. 所以这是我在Java中的尝试:

    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.'}';
    }
    

    有人看到了这个问题吗?我认为不同之处在于: PHP:binary.binary 和 JAVA:addAll(byte [],byte [])

    提前致谢

1 个答案:

答案 0 :(得分:4)

php端的实现是通过第一轮散列然后循环4999次正确地进行5k次迭代。

$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次迭代。

通过在java中以1开始for循环,结果密码哈希值等于。

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