Below is the code of both C# and PHP, I need some help regarding it. I am trying to generate the authenticationKey which is in C# but want the convert in PHP. All is done but I don't know how to implement [System.Text] in PHP as there is hash_hmac() in PHP but what might be the $string in the same function.
C# Version
var hmac = new System.Security.Cryptography.HMACSHA256();
var buffer = userName + accessKey + timeStamp + originUrl;
var hash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(buffer));
var authenticationKey = Convert.ToBase64String(hash);
PHP version
$hmac = hash_hmac('sha256', $string, $buffer);
$encoded = base64_encode($hmac);
Can anyone help me with that. It will be very helpful. Thanks.
I was also searching for this and atlast it was just a mistake of 1 small varibale, in the hash_hmac function pass the last value as true, this is will return raw output and when you convert it to base64 it will give the same output as the c# code.
$buffer = $userName.$accessKey.$timeStamp.$originUrl;
$hmac = hash_hmac('sha256', $buffer, $tokenSecret, true);
$authenticationKey = base64_encode($hmac);
Just in your c# function use the key, as shown in the code below
var hmac = new System.Security.Cryptography.HMACSHA256();
hmac.Key = System.Text.Encoding.UTF8.GetBytes(tokenSecret);
In my code I have tokenSecret variable in bas64 form.
You may get the hex value, using unpack method:
$value = unpack('H*', buffer);
echo $value[1];
In your c# code, you used a randomly generated key. Check the documentation:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hmacsha256?view=netframework-4.8#constructors
HMACSHA256()
Initializes a new instance of the HMACSHA256 class with a randomly generated key.
HMACSHA256(Byte[])
Initializes a new instance of the HMACSHA256 class with the specified key data
Now the php documentation in comparison: https://www.php.net/manual/en/function.hash-hmac.php
hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] ) : string
You see, what you call buffer in php, is actually the key and what you call buffer in c# is the data you hash.
So the$string should contain the data to be hashed.
As per the conversion, you don't need to get bytes in php: What is the PHP equivalent of this C# encoding code?
Related
I am running this code:
var timeStamp = DateTime.UtcNow;
var sharedSecret = "xx";
var saltedString = timeStamp.ToString("2021-01-07T16:42:33.619667Z") + sharedSecret;
//Encoding saltedString using Unicode little-endian byte order
byte[] encodedSaltedString = Encoding.Unicode.GetBytes(saltedString);
//Hashing Algorithm used is SHA512
HashAlgorithm hash = new SHA512Managed();
//Compute Hash of encodedSaltedString
byte[] hashedEncodedString = hash.ComputeHash(encodedSaltedString);
//Convert hashed array to base64-encoded string
string signature = Convert.ToBase64String(hashedEncodedString);
I am then getting this result in C#:
"gQhjrLnY6fo44EeaaWaUBE1PY/8oEIRsUcK3AMSCVUCYMM4vRfxvQEEggXaHTF0GQbw4w2HbWArX1k6NnkzJFg=="
I converted to this code as below, but I am getting an issue. Can I get some help on this?
$timestamp = "2021-01-07T16:42:33.619667Z";
$sharedSecret = 'xx';
$saltedString = $timestamp.$sharedSecret;
$utf=mb_convert_encoding($saltedString, "UTF-16LE");
$signature = base64_encode(hash('sha512', $utf));
IN PHP I am getting this result:
ODEwODYzYWNiOWQ4ZTlmYTM4ZTA0NzlhNjk2Njk0MDQ0ZDRmNjNmZjI4MTA4NDZjNTFjMmI3MDBjNDgyNTU0MDk4MzBjZTJmNDVmYzZmNDA0MTIwODE3Njg3NGM1ZDA2NDFiYzM4YzM2MWRiNTgwYWQ3ZDY0ZThkOWU0Y2M5MTY=
But both should be same. The c# one is correct, I want the same in the php code as well.
From the PHP docs for hash:
hash ( string $algo , string $data , bool $binary = false ) : string|false
binary
When set to true, outputs raw binary data. false outputs lowercase hexits.
You're not passing a value for $binary, so it's returning a string of hexadecimal characters.
The C# HashAlgorithm.ComputeHash method on the other hand binary data.
Since you're base64-encoding the result, you're presumably expecting the hash function to return binary data. You therefore need to pass true as the value for $binary:
$signature = base64_encode(hash('sha512', $utf, true));
I'm not coming up with the same values (using a known password).
I suspect it may be something having to do with encodings, but all the things I've tried haven't worked thus far:
windows code (c#?):
private static string EncodePassword(string password, string salt)
{
string encodedPassword = password;
HMACSHA1 hash = new HMACSHA1 { Key = Convert.FromBase64String(salt) };
encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
return encodedPassword;
}
perl code run on linux:
use Modern::Perl '2015';
use Digest::SHA qw(hmac_sha1 hmac_sha1_base64);
use MIME::Base64 qw(decode_base64 encode_base64);
use Unicode::String qw(utf16be utf16le);
say encode_base64(hmac_sha1($password, decode_base64($salt)));
# (or, equivalently)
say hmac_sha1_base64($password, decode_base64($salt));
my $le16 = utf16le($password);
my $be16 = utf16be($password);
say "ok, try utf-16 (le, then be)...";
say encode_base64(hmac_sha1($le16, decode_base64($salt)));
say encode_base64(hmac_sha1($be16, decode_base64($salt)));
# try reversing the hmac output?
my $hmac_bytes = hmac_sha1($password, decode_base64($salt));
my $rev_bytes = reverse $hmac_bytes;
say encode_base64($rev_bytes);
In the original C# code, in this line:
encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
a call to Encoding.Unicode.GetBytes transforms the password to a byte array via a UTF-16LE encoder.
You have to do the same transformation to get the same hash in Perl:
use Digest::SHA qw(hmac_sha1);
use MIME::Base64 qw(decode_base64 encode_base64);
use Encode qw(encode);
$utf16LEPassword = encode("UTF-16LE", $password);
print encode_base64(hmac_sha1($utf16LEPassword, decode_base64($salt)));
I am trying to replicate the following code in PHP, It is example code for an API I have to interface with (The API & Example code is in C#, My app is in PHP 5.3). I'm not a C# developer and so am having trouble doing this.
// C# Code I am trying to replicate in PHP
var apiTokenId = 1887;
var apiToken = "E1024763-1234-5678-91E0-T32E4E7EB316";
// Used to authenticate our request by the API (which is in C#)
var stringToSign = string.Empty;
stringToSign += "POST"+"UserAgent"+"http://api.com/post";
// Here is the issue, How can I do the following 3 lines in PHP?
// No "secret key" provided?.. How do I do this in PHP?
var hmacsha1 = new HMACSHA1(new Guid(apiToken).toByteArray());
// Make a byte array with ASCII encoding.
byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(stringToSign);
// Finally, 'computeHash' of the above (what does this do exactly?!)
var calculatedSignature = Convert.ToBase64String(hmacsha1.ComputeHash(byteArray));
I've tried many variations using pack() and other functions I've found online, but without anything to compare it to, I don't know if i've done it right or not.
Can any C# devs run the above code and post the values generated so I can use that to check/test against?
I've tried checking the MSDN to see what these methods do, but am stuck (and not sure if its correct, as I have nothing to compare it to).
PHP Pseudo Code
// Set vars
$apiToken = 'E1024763-1234-5678-91E0-T32E4E7EB316';
$apiTokenId = '1887';
$stringToSign = "POST"."UserAgent"."http://api.com/post";
// HowTo: Build a `byteArray` of our apiToken? (i think)
// C#: var hmacsha1 = new HMACSHA1(new Guid(apiToken).toByteArray());
// HowTo: Convert our $stringToSign to a ASCII encoded `byteArray`?
// C#: byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(stringToSign);
// HowTo: Generate a base64 string of our (`hmacsha1`.ComputeHash(byteArray))
// C#: var calculatedSignature = Convert.ToBase64String(hmacsha1.ComputeHash(byteArray));
This sounds pretty simple and straightforwaard, but I'm not sure what a few of these C# methods do..
What do these C# methods do/return?
ComputeHash(byteArray) - Computed to what?.. what is returned?
System.Text.Encoding.ASCII.GetBytes(stringToSign); - What does this return?
new HMACSHA1(new Guid(apiToken).toByteArray()); No Secret Key?, what is the key used?
Any resources or help would be much appreciated.
I tried variations of other answers on SO, but no joy.
Can I run the 3 lines of code somewhere online (like JSFiddle but for C#?) so I can see the output of each line?
Update - Bounty Added
Still having trouble with this, I have managed to test the C# code in Visual Studio, but am having trouble getting the same hash generated in PHP.
I would like...
.. the above C# code (specifically, the 3 lines which create the SHA1 hash) to be converted into PHP (Check out the Pseudo Code I posted above). I should be able to match the C# hash using PHP.
If you have any other questions, please ask.
The issue is that the string form of the GUID reverses the order of the 2-character hexadecimal numbers in the first 3 segments of the GUID. For more information see the comments in the example at: http://msdn.microsoft.com/en-us/library/system.guid.tobytearray.aspx
The following code should work:
$apiTokenId = 1887;
$apiToken = "E1024763-1234-5678-91E0-FF2E4E7EB316";
$stringToSign = '';
$hexStr = str_replace('-','',$apiToken);
$c = explode('-',chunk_split($hexStr,2,'-'));
$hexArr = array($c[3],$c[2],$c[1],$c[0],$c[5],$c[4],$c[7],$c[6],$c[8],$c[9],$c[10],$c[11],$c[12],$c[13],$c[14],$c[15]);
$keyStr = '';
for ($i = 0; $i < 16; ++$i) {
$num = hexdec($hexArr[$i]);
$keyStr .= chr($num);
}
$stringToSign .= "POST" . "UserAgent" . "http://api.com/post";
$hmacsha1 = base64_encode(hash_hmac('sha1',$stringToSign,$keyStr,true));
I've tested this code against the C# code you provided above and the output was the same. However, the GUID specified in the original code is not valid so I had to change it slightly.
It's pretty easy, when i don't have to test the code :P
http://php.net/manual/en/function.hash-hmac.php - that's the equivalent of the HMACSHA1 c# class.
string hash_hmac (string $algo , string $data , string $key [, bool $raw_output = false ] )
So $algo = "sha1"
$data is your $stringToSign - since that is already an ascii string (i hope) - the C# was just taking the byte equivalent of the same.
new Guid(apiToken).toByteArray() -> that's a 16 byte (16*8 = 128) representation of the GUID - which is 32*4 = 128 bits. This is the key.
$key is a string so you need the ASCII string equivalent for your $apiToken (which is 32 hex chars - first strip / ignore the dashes in between) - E10247631234567891E0T32E4E7EB316 (correct the key - it cannot have a "T")
function hex2str($hex) {
for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
return $str;
}
$hexKey = hex2str($apiToken); //strip the dashes first
http://www.linux-support.com/cms/php-convert-hex-strings-to-ascii-strings/
So the method call now works :
$almostResult = hash_hmac ("sha1" , $stringToSign, $hexKey, true)
This returns a binary string - which you need to convert to base64 encoding.
$final = base64_encode ($almostResult)
That should do it...enjoy :)
I faced almost the same problem and after some googling i found this post:
https://www.reddit.com/r/PHP/comments/2k9tol/string_to_byte_array_using_utf8_encoding/
In PHP strings are already byte arrays. What is the specific problem you are having?
For me the solution was just base64_encode('apikey')
I have a hashing method in C# that looks like:
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] raw_input = Encoding.UTF32.GetBytes("hello");
byte[] raw_output = md5.ComputeHash(raw_input);
string output = "";
foreach (byte myByte in raw_output)
output += myByte.ToString("X2");
return output;
How can I implement this in PHP? Doing the following produces a different hash digest...
$output = hash('md5', 'hello');
PHP
This PHP code will do:
<?php
$str = "admin";
$strUtf32 = mb_convert_encoding($str, "UTF-32LE");
echo md5($strUtf32);
?>
This code outputs "1e3fcd02b1547f847cb7fc3add4484a5"
You need to find out which encoding PHP is using to convert your string to text. It's very unlikely that it's using UTF-32. It may well be using the platform default encoding, or possibly UTF-8.
using (MD5 md5 = MD5.Create())
{
byte[] input = Encoding.UTF8.GetBytes("hello");
byte[] hash = md5.ComputeHash(input);
return BitConverter.ToString(hash).Replace("-", "");
}
(This is the problem with languages/platforms which treat strings as binary data all over the place - it doesn't make it clear what's going on. There has to be a conversion to bytes here, as MD5 is defined for bytes, not Unicode characters. In the C# code you're doing it explicitly... in the PHP it's implicit and poorly documented.)
EDIT: If you've got to change the PHP, you could try this:
$text = mb_convert_encoding($text, "UTF-32LE");
$output = md5($text)
It depends whether PHP supports UTF-32 though...
When you apply md5 to Encoding.UTF32.GetBytes("admin");, that's same as
echo hash( "md5","a\0\0\0d\0\0\0m\0\0\0i\0\0\0n\0\0\0");
//1e3fcd02b1547f847cb7fc3add4484a5
In php.
You need to convert your string to UTF32-LE in PHP:
echo md5( mb_convert_encoding( "admin", "UTF-32LE" ) );
//1e3fcd02b1547f847cb7fc3add4484a5
I have the following function in C#, a hashing function which I need converted into PHP. I've tried a few things in PHP but I don't get the same results (I'm not at all good with .NET)
private static string GetSignature(string args, string privatekey)
{
var encoding = new System.Text.ASCIIEncoding();
byte[] key = encoding.GetBytes(privatekey);
var myhmacsha256 = new HMACSHA256(key);
byte[] hashValue = myhmacsha256.ComputeHash(encoding.GetBytes(args));
string hmac64 = Convert.ToBase64String(hashValue);
myhmacsha256.Clear();
return hmac64;
}
One (wrong) attempt in PHP is this:
function encode($data,$key)
{
return base64_encode( hash_hmac('sha256', $data, $key ) );
}
The ANSWER
A slight variation of what was suggested below by DampeS8N worked for me.
function encode($data,$key)
{
iconv_set_encoding("input_encoding", "ASCII");
iconv_set_encoding("internal_encoding", "ASCII");
iconv_set_encoding("output_encoding", "ASCII");
return base64_encode( hash_hmac('sha256', $data, $key, true ) );
}
Please also not the fourth parameter of hash_hmac - now set to true for raw output as binary data
I suspect the very first line of your .net code is the culprit. PHP has no encoding for the string itself and so when it comes time to hash it either is hashing the internal PHP string format's bytes (unlikely, can someone else confirm?) or more than likely is converting to something else first. In this case probably unicode which is decidedly not the same bytes that the string would be in ASCII as .net is requesting.
My advice would be to ensure that PHP is also using ASCII, with iconv, to allow interoperability.
function encode($data,$key)
{
return base64_encode( hash_hmac('sha256', iconv( iconv_get_encoding( "internal_encoding"), "ASCII", $data ), iconv( iconv_get_encoding( "internal_encoding"), "ASCII", $key ) ) );
}
I can't be sure that the above code will output the desired hashes, however, as I don't have .net handy to test the initial code. But this might point you in the right direction.
If this doesn't work, the value inside iconv_get_encoding( might be the culprit, try "output_encoding" or "input_encoding" as well. It is also possible that you'll need to set these same values to ASCII with iconv_set_encoding(.
Good Luck!
Update! This is what ultimately worked:
function encode($data,$key)
{
iconv_set_encoding("input_encoding", "ASCII");
iconv_set_encoding("internal_encoding", "ASCII");
iconv_set_encoding("output_encoding", "ASCII");
return base64_encode( hash_hmac('sha256', $data, $key, true ) );
}