Avatar of adworldmedia
adworldmedia
Flag for United States of America asked on

How to Generate HMAC SHA256 in PHP?

I've coded a token generation routine in C#, that I need to convert to PHP and am having trouble.

The C# code is:
        public string GenerateToken(string username, string password, string ip, string userAgent, long ticks)
        {
            authSettings setting = getSettings();
            string hash = string.Join(":", new string[] { username, ip, userAgent, ticks.ToString() });
            string hashLeft = "";
            string hashRight = "";
            using (HMAC hmac = HMACSHA256.Create(setting.alg))
            {
                hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password,setting));
                hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
                hashLeft = Convert.ToBase64String(hmac.Hash);
                hashRight = string.Join(":", new string[] { username, ticks.ToString() });
            }
            string genToken = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
            return genToken;
        }

        public string GetHashedPassword(string password, authSettings setting)
        {
            string key = string.Join(":", new string[] { password, setting.salt });
            using (HMAC hmac = HMACSHA256.Create(setting.alg))
            {
                hmac.Key = Encoding.UTF8.GetBytes(setting.salt);
                hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
                return Convert.ToBase64String(hmac.Hash);
            }
        }

Open in new window


and the PHP code I have is:
//Define key to hash, password + salt
$key = base64_encode(hash_hmac('sha256', utf8_encode($password . ':' . $salt), utf8_encode($salt)));
echo '<b>Key:</b> ' . $key . '<br>';

//Define Message
$message = $username . ':' . $ip . ':' . $agent . ':' . $timestamp;

//Create Hash
$hash = hash_hmac('sha256', $message, $key);

//Create token
$token = base64_encode($hash);

$tokenId = $username . ':' . $timestamp;
$tokenString = $token . ':' . $tokenId;
$tokenRetVal = base64_encode($tokenString);

echo '<b>Token String:</b> ' . $tokenString . '<br>';
echo '<b><font color=red>Token:</font></b> ' . $tokenRetVal . '<br>';

Open in new window


The results of the idential values, are completely different.  What am I doing wrong?
PHPC#ASP.NET

Avatar of undefined
Last Comment
adworldmedia

8/22/2022 - Mon
Phil Davidson

Wouldn't the different salt values make the results different?  Are the relevant keys exactly the same?  If the servers are different for the C# version vs the PHP version, they may use different keys to begin with.
adworldmedia

ASKER
The salt is the same in both implementations.  And the server running the c# is Windows and the PHP one is Linux.
Phil Davidson

Isn't the hash the result of a RSA or SHA key on the server?   Maybe it is over my head.  It seems like different servers would have different private keys and thus generate different hash values.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
gr8gonzo

You're probably double-encoding your UTF-8 characters.

Encoding.UTF8.GetBytes() isn't encoding the string to UTF-8 - it is simply returning the byte array that represents that string that is presumably already UTF-8 encoded.

However, in PHP, utf8_encode() assumes that the source string is in an ISO-8859-1 format (see my article on this - https://www.experts-exchange.com/articles/25999/Unicode-UTF-8-and-Multibyte-in-Plain-English.html) and will convert any characters that fall outside the normal ASCII range to their UTF-8 equivalents. So if your source string is already encoded as UTF-8 and contains such characters, then you are actually corrupting your string (which would, of course, lead to a different result).

I would suggest taking out the utf8_encode() calls altogether in PHP, assuming that your source values are already encoded.

Or if you just want to do a simple test to confirm this is the problem, use a simple plain ASCII string (alphanumeric characters only) for your key/salt on both sides and then test.

Also, double-check and compare your timestamp and ticks. Ticks in C# are different than the timestamp in PHP (I can't see where the $timestamp value comes from).

Oh, and finally, hash_hmac() will return a hex-encoded hash UNLESS you specify "true" as the 4th parameter. Adding that "true" parameter will result in you getting the raw bytes, which is what you get in C#. So that's another possible place where the differences could occur.
ASKER CERTIFIED SOLUTION
gr8gonzo

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
adworldmedia

ASKER
This worked perfectly!  Thanks!!