简体   繁体   中英

PHP hash_hmac() OS difference

I need to sign my Amazon Product API request, the accepted code to do this is the following.

base64_encode(hash_hmac('sha256', $request, $key, true));

This works great when I'm on 64bit Linux but fail when I'm on 64bit Windows 7. Has anyone got any ideas how to debug why hash_hmac provides different output between 64bit Linux and 64bit Windows?

Test code:

<?php
$test = "GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06";
#echo $test;
echo base64_encode(hash_hmac('sha256', $test, '1234567890', true));

Edit The correct output of the code above is Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg= The incorrect output is X0UTct9XSJ/3k2gu6noyKhTlksx5ZbH4qAbCyW3zX48=

The non-base64-encoded values are 5f451372df57489ff793682eea7a322a14e592cc7965b1f8a806c2c96df35f8f for the incorrect and 35a71ef94dc0cf83a137bb484aa82cd6f74b0470448a359c05e0aa2f9c4df718 for the correct one when converted to hex. (it's raw binary output in the call)

Edit 2 My guess right now is that even though the Windows 7 OS is 64bit the PHP binary is compiled as 32bit as I could not find a 64bit version on the php.net site.

Edit 3 I'm actually pretty sure now that the problem is that the binary is 32bit. Most likely PHP on linux uses an OS version to calculate the hash while PHP on windows implements it's own version which causes the 32/64bit difference even though both OS are 64bit.

Congratulations, and welcome to the wonderful world of line endings. The problem you're having is a classic one: line endings in *nix are \\n , and line endings in windows are \\r\\n . Try the following instead:

$test = "GET\n".
"webservices.amazon.com\n".
"/onca/xml\n".
"AWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06";

Please note that with respect to Amazon Seller Web Service, the order of the parameters thats in your post string also matters, it seems they have a sort order which they maintain internally to all the post params, which is used to prepare the string that is used to generate the signature. if you use the sketchpad you can get the order of the params, i did not notice it on any documentation.

You can verify the output with mhash() . It follows the same algorithm.

 hash_hmac("sha256", "data", "key");

is equivalent to

 bin2hex(mhash(MHASH_SHA256, "data", "key"));

So if there is no difference between these two, I surmise the error lies in the implementation of sha256. Either way, it's a bug. Report on php.net and demand a new build.

You could also test with hash("sha256", "test"); on both platforms to debug further which one works correctly. "test" should become 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

Apologies for leaving the question open since I did figure out the problem, and like most problems usually do it turned out to be PEBCAC.

The answer is not 32 versus 64bit but rather line endings. Linux has \\n while Windows has \\r\\n.

Write it all out on one line and explicitly use \\n and it should work.

$test = "GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06";
echo base64_encode(hash_hmac('sha256', $test, '1234567890', true));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM