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!