用PHP重新生成ASP SHA1 salt / hash

时间:2017-05-16 18:23:37

标签: php asp.net hash

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

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

我一直在使用https://forums.asp.net/p/1336657/2899172.aspx作为编写PHP脚本来重现哈希的参考。这是我尝试直接重写的片段:

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

看起来很简单 - 获取密码的字节数组,base64-decode salt,连接它们,然后通过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

1 个答案:

答案 0 :(得分:2)

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

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

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

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

这只需要盐,对其进行解码,然后通过在每个字符后面添加一个零字节将密码转换为UTF16。