5

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.

Community
  • 1
  • 1
  • 1
    Did you read the documentation for any of the questions that you've asked? 1) ComputeHash hashes the byte array, in this case using HMACSHA1. 2) Encoding.ASCII.GetBytes returns the string, encoded in ASCII (as bytes). 3) You're providing the key to the constructor of HMACSHA1. – Jon Skeet Jun 25 '13 at 18:56
  • 1
    var apiToken = "E1024763-1234-5678-91E0-T32E4E7EB316"; A typo there? GUIDs can't have a "T" - all hex :) – Vivek Jun 25 '13 at 18:58
  • @JonSkeet I figured that out soon after asking the question, my trouble was doing the equivalent in PHP, I knew I needed the hash_hmac func, but no idea I needed hexdec, thinking about this now, this was more a PHP related question only, –  Jun 26 '13 at 10:27
  • I just wanted to mask out my private one, I replaced it with some random chars, you have a very keen eye ;) –  Jun 26 '13 at 10:27

3 Answers3

3

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.

AlliterativeAlice
  • 11,841
  • 9
  • 52
  • 69
0

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 :)

Vivek
  • 2,103
  • 17
  • 26
0

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')

Robert Fridzema
  • 517
  • 3
  • 18