PBKDBF2 hash - duplicating functionality of .NET r

2019-08-31 15:17发布

问题:

I have a .NET web site built using MVC that has a user-management module. This module generates user passwords hashed conforming to the RFC2898 spec and stores them in a db. The specific implementation I'm using is the quick and convenient method found in System.Web.Helpers.Crypto.HashPassword().

Looking at the source code for that method as found here, it is wrapping a call to the Rfc2898DeriveBytes class, which actually creates the hash.

So far so good. The problem I'm facing is that we have a web service written in PHP that must do the actual user authentication. This means it must be able to receive a raw password from a user and generate the same hash.

The NET Crypto.HashPassword() function creates a base64 encoded string that contains the salt as well as the key. We are correctly (I think) extracting this data in the below PHP implementation so the salt input should be the same.

Both implementations also perform 1000 iterations.

Our PHP implementation:

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

So my question is: Can anyone tell me if this PHP implementation supposed to generate the same output as the .NET Rfc2898DerivedBytes implementation? They are both supposedly conforming to RFC2898.

I'm not a domain expert in this subject matter, so it's possible we're missing something obvious here..

Thanks in advance!

回答1:

1) Crypto.HashPassword() uses SHA1 and your PHP code uses SHA256. 2) The password needs to be UTF8-encoded (doesn't matter for plain latin passwords, but do it anyway).

Also, you $hashed_pass is incorrect (it does NOT correspond to "Welcome4" given the salt). Please, never do such things. It wasted a lot of my time. An example of real hash for Welcome4" is ALtF3x2vx6u6gJIOm1MdTNwvL4yNBeKjuwscgkLgJUAI9TE2N8nYTjanAuXJjcqYpA==. Try it.

So, the correct call is:

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


标签: c# php hash pbkdf2