【问题标题】:Regenerate ASP SHA1 salt / hash with PHP使用 PHP 重新生成 ASP SHA1 salt / hash
【发布时间】:2017-10-15 23:25:51
【问题描述】:

我们有一个拥有数千个旧密码哈希的客户,他们希望我们能够验证/匹配这些密码,以便将这些登录信息转移到更现代的安全考虑中。

哈希值类似于 SHA1(十六进制和 40 个字符),并且是在 ASP.NET 中生成的。我们有一些测试用例包含明文密码、base64 编码的 salt 和 base64 编码的哈希 - 我们需要能够根据给定的密码和 salt 重现哈希。

我一直使用https://forums.asp.net/p/1336657/2899172.aspx 作为参考来编写一个PHP 脚本来重现散列。这是我试图直接重写的sn-p:

public string EncodePassword(string pass, string saltBase64)
{
    byte[] bytes = Encoding.Unicode.GetBytes(pass);
    byte[] src = Convert.FromBase64String(saltBase64);
    byte[] dst = new byte[src.Length + bytes.Length];
    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
    HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
    byte[] inArray = algorithm.ComputeHash(dst);
    return Convert.ToBase64String(inArray);
}

看起来很简单 - 获取密码的字节数组,对 salt 进行 base64 解码,将它们连接起来,然后通过 SHA1 运行它们。我觉得我已经完成了 90% 的工作,但是无论我尝试什么变体,我都无法成功匹配提供的哈希!

我怀疑我复制 Encoding.Unicode.GetBytes 的尝试是问题所在 - unpack 可能不等效。但是经过一个小时左右的搜索,我找不到任何靠近的方法。我不是 ASP 开发人员,我敢肯定我遗漏了一些细微差别 - 谁能指出哪里出了问题?

这是我的测试输出代码:

$test = [
    'plaintext' => 'overthis2:)',
    'salt' => '7LeJR68EGhBgaE7EYgL/gg==',
    'hash' => 'g54KVV4Wj5smOyXHOReyWnTnGDc='
];
$algos = [ 'sha1', 'ripemd160', 'tiger160,3', 'tiger160,4', 'haval160,3', 'haval160,4', 'haval160,5' ];
$raw = implode( '', unpack( "H*", $test['plaintext'] ) ) . base64_decode( $test['salt'] );

echo 'Plaintext:     ' . $test['plaintext'] . "\n";
echo 'Salt:          ' . $test['salt'] . "\n";
echo 'Raw:           ' . $raw . "\n\n";
echo 'Saved hash:    ' . $test['hash'] . "\n";
echo 'Decoded:       ' . bin2hex( base64_decode( $test['hash'] ) ) . "\n\n";

foreach ( $algos as $algo ) {
    echo str_pad( $algo . ':' , 15, ' ' ) . hash( $algo, $raw ) . "\n";
}

这是它得到的结果 - 没有一个哈希与提供的匹配:

Plaintext:     overthis2:)
Salt:          7LeJR68EGhBgaE7EYgL/gg==
Raw:           6f76657274686973323a29췉G�`hN�b��

Saved hash:    g54KVV4Wj5smOyXHOReyWnTnGDc=
Decoded:       839e0a555e168f9b263b25c73917b25a74e71837

sha1:          d0b448e50d81e6a42601ebcc8e7aa07423d12210
ripemd160:     4359afa37173388db43c47db5188cf5cc47f30d9
tiger160,3:    c2e22500127cef7141077049e9bda7747e0b298d
tiger160,4:    e03fc57f1d9259cd650aab3682cf54609a30d62b
haval160,3:    437838726fc63fb15969e1c5f0b34dc7f404cabc
haval160,4:    ec5cb7e38fe28534b65d25fabf756113c9e563d6
haval160,5:    48551660bbc519bb7db8d595556f4f167eeec749

【问题讨论】:

  • 因为它是您无法复制的哈希值。您可以做的是设置表以保存另一列,检查它们是否有新的哈希。如果不是,请检查无哈希或旧哈希,然后使用password_hash() 创建一个新哈希。
  • @JayBlanchard 它应该为给定的输入产生相同的哈希值(如果正确),对吧? SHA1-ing test 将始终给出 a94a8fe5ccb19ba61c4c0873d391e987982fbbd3。见this answer
  • @JayBlanchard 感谢您的输入,但事实并非如此——SHA1 是一对一的转换,相同的字符串总是产生相同的哈希值。我只是缺少 ASP.NET 如何创建预散列密码/盐的组件。您能否深入了解我所缺少的内容?
  • 你是对的——从 sha1 到 sha1 确实产生了同样的结果。我应该更清楚,因为对我来说,哈希是用盐产生的。快速研究表明,其他人也无法生产这种产品。
  • 我认为你在最后用 PHP 和 ASP.NET 把它放在了前面

标签: php asp.net hash


【解决方案1】:

.NET 代码将密码处理为 UTF16LE 字符串 (Encoding.Unicode),因此每个字符都是 16 位宽。这意味着在这种情况下,该部分中每隔一个字节都为零。

你也有错误的盐和密码,不需要解压任何东西,.NET 代码不使用十六进制值,而是原始字节。

进行此更改,您将获得相同的哈希值:

$raw = base64_decode( $test['salt'] ).implode("\0", str_split($test['plaintext']))."\0";

这只是获取盐,对其进行解码,然后通过在每个字符后添加一个零字节将密码转换为 UTF16,仅此而已。

【讨论】:

  • 这确实完美地解决了我的问题 - 非常感谢!
猜你喜欢
  • 2013-06-17
  • 1970-01-01
  • 2017-03-27
  • 2012-07-05
  • 2019-12-13
  • 1970-01-01
  • 2010-12-16
  • 2012-10-18
  • 2019-05-23
相关资源
最近更新 更多