I need to convert VerifyPasswordHash method in C# to php. I have salt and stored_hash in DB for this user.
stored_hash = 0x0C3F6C5921CCD0305B2EDEDE1553B1DF55B87A9D55FEE3384A3833611BC40D106BBB48CCE1093AE35B9D0E3A1FE62E86186A6EC143BA00E53945E99C259B4913
salt=0xC62C5A645280DBCC615ED4A3E861D800B00A929856A9664B3AED50A06481ED19AFB09F74D3D7A9EA25327D93F23FDFBD2DE8CF3A75D65A3EA97290E0486F1F4322D2B5853AE6FE848E50355C35B62A993CF6689D9F9ABC861C5E7D88B099617E6A6C7792E285EFBB809FD69CD926C9BD9129AD1BE7DDB5DD459C2B9A2B945B31
Here is example in C#:
`
private bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i )
{
if (computedHash[i] != storedHash[i])
{
return false;
}
}
}
return true;
}
Here is what I tried in PHP: `
<?php
$password = 'Password202!';
$pass_hash = '0x0C3F6C5921CCD0305B2EDEDE1553B1DF55B87A9D55FEE3384A3833611BC40D106BBB48CCE1093AE35B9D0E3A1FE62E86186A6EC143BA00E53945E99C259B4913';
$pass_salt = '0xC62C5A645280DBCC615ED4A3E861D800B00A929856A9664B3AED50A06481ED19AFB09F74D3D7A9EA25327D93F23FDFBD2DE8CF3A75D65A3EA97290E0486F1F4322D2B5853AE6FE848E50355C35B62A993CF6689D9F9ABC861C5E7D88B099617E6A6C7792E285EFBB809FD69CD926C9BD9129AD1BE7DDB5DD459C2B9A2B945B31';
function VerifyPasswordHash($password,$pass_hash,$pass_salt){
$password_utf8 = utf8_encode($password);
$sig = hash_hmac('sha512', $pass_salt, $password_utf8, true);
echo base64_encode($sig);
}
VerifyPasswordHash($password,$pass_hash,$pass_salt);
I know that i need to compare $sig and stored_hash but they don`t even match.. Any help would be appreciated!
Expected result is successful comparison of $pass_hash and computed_hash. I simply need to verify password
CodePudding user response:
There are a few things wrong with your code, but you're almost there.
Firstly, drop the utf8_encode()
. This rarely does what you expect and, as @Chris mentioned in a comment, the function has been deprecated and will be removed in the future.
According to the manual, the data to hash comes before the key (salt), so you have your parameters in the wrong order:
$sig = hash_hmac('sha512', $password, $pass_salt, true);
// $password first, then $pass_salt
Next, you're using base64_encode
. That is not what you want, you're looking for a hexadecimal representation of the created signature. You're looking for bin2hex()
:
echo bin2hex($sig);
However, this has the same effect as just passing false
as the final argument to hash_hmac
-- the true
argument indicates you want a binary result, then you're turning it into hexadecimal representation. Passing false
will return the hexadecimal notation instead of a binary result so you can skip the bin2hex
step:
$sig = hash_hmac('sha512', $password, $pass_salt, false);
echo $sig;
The final problem is the notation of the salt. PHP does not use the 0x
prefix for hexadecimal strings. After a bit of trial-and-error, I also found that the correct way to calculate the hash is to actually use the binary representation of the hexadecimal salt-string (using hex2bin
, the reverse of bin2hex
):
function VerifyPasswordHash($password,$pass_hash,$pass_salt){
// The substr() removes the leading "0x" from the salt string.
$sig = hash_hmac('sha512', $password, hex2bin(substr($pass_salt, 2)), false);
echo $sig;
}
For the values you provided, this outputs:
0c3f6c5921ccd0305b2edede1553b1df55b87a9d55fee3384a3833611bc40d106bbb48cce1093ae35b9d0e3a1fe62e86186a6ec143ba00e53945e99c259b4913
This matches your sample hash, if you'd uppercase it and put the redundant "0x" in front of it:
function VerifyPasswordHash($password,$pass_hash,$pass_salt){
$sig = hash_hmac('sha512', $password, hex2bin(substr($pass_salt, 2)), false);
$hash = '0x' . strtoupper($sig);
return $hash === $pass_hash;
}
This returns true
for the given sample hashes.