【问题标题】:Creating same hashes like symfony2 does with java (SHA-512)使用 java (SHA-512) 创建与 symfony2 相同的哈希值
【发布时间】:2016-08-30 13:30:06
【问题描述】:

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

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

听起来很简单,正常情况下它像往常一样工作,但是这个 Symfony 和 PHP 让我抓狂。

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

重要的方法有:

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

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

这似乎是直截了当的,但我坚持下去。 当我读到这篇文章时,它确实如此:

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

这是我在 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));
}

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

提前致谢

【问题讨论】:

  • 请您使用 BouncyCastleProvider 的原因
  • 因为我写这个弹跳城堡的时间是每个想要一个具有良好默认值的安全提供商的事实上的标准。 Nova 天我会使用 openssl impl。

标签: java php symfony hash sha


【解决方案1】:

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

【讨论】:

  • 听起来不错,而且很有道理;)我明天试试。今天刚写完
  • 在PHP代码中,如果$this-&gt;iterations5,000并且$i在for循环中被初始化为1,那么for循环将迭代4,999次,而不是@987654328 @时代。既然你说第一轮是在 PHP 代码的循环之外完成的,那么 PHP 和 Java 代码都应该迭代 5,000 次。
  • 你是对的,PHP 代码确实迭代了 5000 (4999 + 1) 次。但是,Java 代码确实迭代了 5001 次 (5000 + 1),因为它还有循环之外的初始轮次。我在回答中混淆了两种语言,对不起。我会解决的。不过,我仍然认为这是问题的根源。
  • 不是最好的答案 ;) 但这是重点。我一直在阅读,在 php 代码中循环从 1 开始而不是 0,因为我们想要 5k 循环而不是 +1 ;) 我将关于 php 代码的代码更改为从 1 开始,现在我的结果哈希是正确的。我可以对我的测试密码进行编码。非常感谢
  • 我将您的帖子编辑发送到正确答案。我对全文进行了大修。希望你没事。它仍在审核中,可能不被接受。
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2014-08-19
  • 1970-01-01
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多