PBKDBF2哈希 - PHP 中 .NET rfc2898DerivedBytes 类的复制功能


PBKDBF2 hash - duplicating functionality of .NET rfc2898DerivedBytes class in PHP

我有一个使用 MVC 构建的 .NET 网站,它有一个用户管理模块。此模块生成符合RFC2898规范的散列用户密码,并将其存储在数据库中。我使用的具体实现是System.Web.Helpers.Crypto.HashPassword()中找到的快速方便的方法。

查看此处找到的该方法的源代码,它包装了对 Rfc2898DeriveBytes 类的调用,该类实际上创建了哈希。

目前为止,一切都好。我面临的问题是,我们有一个用PHP编写的Web服务,它必须执行实际的用户身份验证。这意味着它必须能够从用户那里接收原始密码并生成相同的哈希。

NET Crypto.HashPassword() 函数创建一个包含盐和键的 base64 编码字符串。我们在下面的 PHP 实现中正确(我认为)提取了这些数据,因此盐输入应该是相同的。

这两种实现还执行 1000 次迭代。

我们的 PHP 实现:

    function hashtest(){ 
               //password from user
                $pass = "Welcome4";
                //hash generated by Crypto.HashPassword()
                $hashed_pass = "AP4S5RIkCIT1caoSUocllccY2kXQ5UUDv4iiPCkEELcDK0fhG7Uy+zD0y0FwowI6hA==";
                $decode_val = base64_decode($hashed_pass);
                echo "<br/>Decoded value is: ".$decode_val;
                echo "<br/>Length of decoded string is: ".strlen($decode_val);
                $salt = substr($decode_val, 1, 16);
                $key = substr($decode_val, 17, 49);
                echo "<br/>Salt is: ".$salt."<br/>Key is: ".$key;
                $result = $this->pbkdf2("sha256", $pass, $salt, 1000, 32, true);
                echo "<br/>PBKDF2 result is: ".$result;
                if($key == $result)
                        echo "<br/>MATCHED!";
                else
                        echo "<br/>NOT MATCHED";
And the pbkdbf2 implementation:
                function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false){    
                    $algorithm = strtolower($algorithm);
                    if(!in_array($algorithm, hash_algos(), true))
                        die('PBKDF2 ERROR: Invalid hash algorithm.');
                    if($count <= 0 || $key_length <= 0)
                        die('PBKDF2 ERROR: Invalid parameters.'); 
                    $hash_length = strlen(hash($algorithm, "", true));
                    $block_count = ceil($key_length / $hash_length);
                    $output = "";
                    for($i = 1; $i <= $block_count; $i++) {
                        // $i encoded as 4 bytes, big endian.
                        $last = $salt . pack("N", $i);
                        // first iteration
                        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
                        // perform the other $count - 1 iterations
                        for ($j = 1; $j < $count; $j++) {
                            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
                        }
                        $output .= $xorsum;
                    } 
                    if($raw_output)
                        return substr($output, 0, $key_length);
                    else
                        return bin2hex(substr($output, 0, $key_length));
                }

所以我的问题是:谁能告诉我这个PHP实现是否应该生成与.NET Rfc2898DerivedBytes实现相同的输出?据说他们都符合RFC2898。

我不是这个主题的领域专家,所以我们可能在这里错过了一些明显的东西。

提前感谢!

1) Crypto.HashPassword()使用 SHA1,而您的 PHP 代码使用 SHA256。2)密码需要采用UTF8编码(对于纯拉丁密码无关紧要,但无论如何都要这样做)。

此外,您$hashed_pass不正确(鉴于盐,它与"Welcome4"不对应)。拜托,永远不要做这样的事情。它浪费了我很多时间。Welcome4"的真实哈希示例是ALtF3x2vx6u6gJIOm1MdTNwvL4yNBeKjuwscgkLgJUAI9TE2N8nYTjanAuXJjcqYpA==。试试吧。

因此,正确的调用是:

$result = $this->pbkdf2("sha1", utf8_encode($pass), $salt, 1000, 32, true);