Amazon AWS API REST Authentication for PHP 5
Since Amazon decided all of their requests needed to be authenticated, developers have been scrambling to convert their existing code to work with their new authentication architecture.
Here’s an excerpt of the email you probably received:
“… signatures will be necessary to authenticate each call to the Product Advertising API. This requirement will be phased in starting May 11, 2009, and by August 15, 2009, all calls to the Product Advertising API must be authenticated or they will not be processed. For pointers on how you can easily authenticate requests to the Product Advertising API, please refer to the developer guide, available here.”
You can find the developer guide documentation they are referring to here:
Product Advertising API
The documentation is very thorough and complete, but doesn’t get to the gist of the problem: what do I need to do in order to make my existing request, still work?
After some research, I came across the work of a couple of individuals, made some slight modifications, and have come up with a function that should help you. Here are the works I am referring to:
Amazon Product Advertising API Signature – PHP
REST Authentication for PHP4
If you’re using PHP and the REST architecture, then you’ve probably got something in your script that creates your URI and fires it off with a file_get_contents and simplexml_load_string.
In this case, you’ve probably already got everything together to create your nice URI. It may look something like this:
$uri = "http://webservices.amazon.com/onca/xml?" . "Service=AWSECommerceService" . "&Operation=ItemSearch" . "&MerchantId=All" . "&Condition=All" . "&Availability=Available" . "&Sort={$sort_by}" . "&Version={$amazon_version}" . "&SubscriptionId={$amazon_subscription_id}" . "&AssociateTag={$amazon_associate_tag}" . "&{$amazon_search_type}={$search_title}" . "&SearchIndex={$search_database}" . "&ResponseGroup={$information_requested}";
The new requirements allow you to still use your old code, but there are modifications necessary. A date needs to be added, a signature needs to be created and the keys/URI have to be ordered/formatted correctly with all the necessary character replacements in order for everything to work. It seems like it could be quite a task to write something that works very differently from your previous code.
In the end, I was able to take my original URI and feed it into a function, and let the code continue along it’s merry way. Here’s what I came up with:
/** * This function will take an existing Amazon request and change it so that it will be usable * with the new authentication. * * @param string $secret_key - your Amazon AWS secret key * @param string $request - your existing request URI * @param string $access_key - your Amazon AWS access key * @param string $version - (optional) the version of the service you are using */ function getRequest($secret_key, $request, $access_key = false, $version = '2009-03-01') { // Get a nice array of elements to work with $uri_elements = parse_url($request); // Grab our request elements $request = $uri_elements['query']; // Throw them into an array parse_str($request, $parameters); // Add the new required paramters $parameters['Timestamp'] = gmdate("Y-m-d\TH:i:s\Z"); $parameters['Version'] = $version; if (strlen($access_key) > 0) { $parameters['AWSAccessKeyId'] = $access_key; } // The new authentication requirements need the keys to be sorted ksort($parameters); // Create our new request foreach ($parameters as $parameter => $value) { // We need to be sure we properly encode the value of our parameter $parameter = str_replace("%7E", "~", rawurlencode($parameter)); $value = str_replace("%7E", "~", rawurlencode($value)); $request_array[] = $parameter . '=' . $value; } // Put our & symbol at the beginning of each of our request variables and put it in a string $new_request = implode('&', $request_array); // Create our signature string $signature_string = "GET\n{$uri_elements['host']}\n{$uri_elements['path']}\n{$new_request}"; // Create our signature using hash_hmac $signature = urlencode(base64_encode(hash_hmac('sha256', $signature_string, $secret_key, true))); // Return our new request return "http://{$uri_elements['host']}{$uri_elements['path']}?{$new_request}&Signature={$signature}"; }
I hope others find this helpful. If you have any comments or changes to the code, feel free to submit them below.
If you liked this post, then please consider subscribing to my feed.
Site submission links:
![]() |
![]() |
Related Posts:




August 4th, 2009 at 9:53 am
There’s an error in your code –
$signature = urlencode(base64_encode(hash_hmac(’sha256′, $secret_key, $signature_string)));
should read
$signature = urlencode(base64_encode(hash_hmac(’sha256′, $signature_string, $secret_key)));
(the order of arguments was wrong)
August 4th, 2009 at 10:40 am
Michael;
Right you are…
Gave me a scare. I checked our production code and it was, in fact, in the correct order.
Must’ve fixed it there and not here.
Thanks much!
August 7th, 2009 at 12:11 am
Nice one. Thanks for sharing!
August 11th, 2009 at 12:36 am
For some reason, I got a signature error when using the code.
So I changed the line:
from
// calculate HMAC with SHA256 and base64-encoding
$signature = base64_encode(hash_hmac(“sha256″, $string_to_sign, $private_key));
to
// calculate HMAC with SHA256 and base64-encoding
$signature = base64_encode(hash_hmac(“sha256″, $string_to_sign, $private_key, True));
Then it works!
Note: the “True” parameter is for output raw binary data.
Thanks for the code!
August 11th, 2009 at 5:00 pm
I have been looking for code like this. I can run it without getting errors, but how do I get it to print the URL. I need to know how to return an output from the function so I can run a script that parses the xml! thanks.
August 13th, 2009 at 3:12 am
Thank’s a lot. That was very useful.
August 13th, 2009 at 4:13 am
Like afterfate I had to set the raw_output parameter of hash_mdac() to TRUE for it to work.
August 13th, 2009 at 5:27 am
Not work for me eighter with or without
$signature = base64_encode(hash_hmac(”sha256″, $string_to_sign, $private_key));
$signature = base64_encode(hash_hmac(”sha256″, $string_to_sign, $private_key, True));
I did it in perl, and I start perl with system()
August 15th, 2009 at 7:02 pm
That was immensely helpful. Thanks! I also needed the TRUE for hash_mdac() to get it working.
afterfate played with the code before he posted his fix. For the record, the fix should be this:
$signature = urlencode(base64_encode(hash_hmac(’sha256′, $signature_string, $secret_key, TRUE)));
August 16th, 2009 at 8:46 am
thanks, this helped a lot. for it to work for me, i needed TRUE for hash_mdac() and I also had to change the timestamp to:
$parameters['Timestamp'] = gmdate(“Y-m-d\TH:i:s.000\Z”);
instead of
$parameters['Timestamp'] = gmdate(“Y-m-d\TH:i:s\Z”);
August 17th, 2009 at 1:45 am
Cheers everyone!
August 18th, 2009 at 1:21 pm
Hi,
Just starting out in php/aws programming.
My crafted (heavily cut and pasted from various sources) request to amazon just started getting refused by amazon.co.uk.
Your code looks like it might help me, but I don’t really understand how to implement it, feed uri into function??
My code displays the amazon wishlist of a user with an amazon wishlist id = &listid
I would love some help in adding the authentication parameters to my request.
Thank you in advance
Duncan
My code at the moment (in a drupal custom views field):
August 18th, 2009 at 1:52 pm
Hello Duncan;
From the looks of things, you should be able to use this function with your code.
The line where your request in crafted will probably be where you will want to use the function posted here. Before doing:
You should have:
I think that should get you what you’re looking for.
August 19th, 2009 at 12:20 am
Holy crap, that worked!! Brilliant thankyou. I added the $yoursecretamazonket and $your amazonaccesskey variables at the top of my original code, changed my $response line to your suggestion and cut and pasted your function to the top of my entire code. Put that in a block, and it worked, got my wishlists back! thankyou.
P.S with my newfound enthusiasm I tried to fix another broken part of my site where a user enters his amazon account email address and my site returns his wishlist id(s).
the my-amzemail comes from a submit form on my Drupal Wishlist Panel page,
(
)
which posts the users email address back to the Wishlist Panel page within Drupal
the code below takes that email and creates an amazon request which was working before, so I added my 2 new variables, swapped my $response line and pasted the function above the whole code.
Didn’t work though
I’ll keep plugging away but any ideas are appreciated.
Cheers
Duncan
<?php
print 'Your Wishlist(s) ‘ ;
$listemail=$_POST['my-amzemail'];
$request = ‘http://ecs.amazonaws.co.uk/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAJ6VMM6PWOTPZVCMA&Operation=ListSearch&ListType=WishList&Email=’. $listemail .”;
$response = file_get_contents($request);
if (!$response) {
die(‘Web service request failed’);
}
$xml = new SimpleXMLElement($response);
?>
Lists;
foreach($lists->List as $list){
$ListURL = $list->ListURL;
$ListName = $list->ListName;
$TotalItems = $list->TotalItems;
$ListId = $list->ListId;
print(“$ListName ID no.($ListId) has $TotalItems items.”);
}
?>
August 19th, 2009 at 12:52 am
I took out the chevrons
form action=”Wishlist” method=”post”
input type=”text” id=”my-amzemail” name=”my-amzemail” /
input type=”submit”
/form
August 19th, 2009 at 10:05 am
I’m using your code and the response coming back from Amazon says that I have the wrong format for the Timestamp. It says:
Correct format is (ISO 8601 UTC) “YYYY_MM_DDThh:mm:ssZ
I’ve tried:
Y-m-d\TH:i:s.000\Z
Y-m-d\TH:i:s\Z
Y_m_d\TH:i:s.000\Z
Y_m_d\TH:i:s\Z
The encoded param looks right: 2009_08_19T17%3A01%3A35Z
And a bunch of other combinations and it doesn’t seem to work. I’m trying to do a ListSearch for wishlists on the ecs.amazonaws.com US server.
Thanks,
Brian
August 19th, 2009 at 11:24 am
Ah, turns out I was passing the urlencoded parameters to my rest client (Zend) and it was encoding them again and corrupting the request.
I inserted a line at the end of the code that adds a decoded signature to the sorted unencoded parameter array:
$parameters['Signature'] = urldecode($signature);
And then I return those parameters to my REST client to send.
Thanks for the great code!
August 20th, 2009 at 9:04 am
Thanks works like a charm
August 25th, 2009 at 12:42 am
OK, got it, stupid typo, had my ‘ and ” all over the wrong place in the $request.
Works fine now, thanks again
Duncan
September 2nd, 2009 at 1:16 pm
Thanks for the great script.
Got it working on my laptop in under 5 minutes.
Only problem is when I uploaded the code to my Linux server it does not authenticate.
I have been unable to spot any platform specific functions in the PHP script.
The only differences from inspecting the variables is that the $signature variable comes out 8 characters longer on the Windows machine than the Linux machine.
Any thoughts?
Anyone else had the same issue?
October 1st, 2009 at 4:32 pm
Worked like a charm, thank you so much!
October 6th, 2009 at 8:59 am
You Rock!. I have been trying to get my code to work and here it is. You’ve done it! and it works.
Thanks a Bunch!
October 13th, 2009 at 11:38 am
Well, your code produces no errors for me, but I can’t get it to print the result of the request. Where should I put a print_r() so that I can see what I get? thanks.
October 22nd, 2009 at 12:21 pm
I have some problems with $newrequest
i try this
$newrequest = getRequest($secret_key, $request, $access_key = false, $version = ‘2009-03-01′);
then i get a new request with secretkey & timestamp but i still Error when i open a new request on browser
“is not a valid value for Signature. Please change this value and retry your request.”
someone help please(i’m noob)
thank you.
December 1st, 2009 at 4:41 pm
Thanks for the great script! You saved me weeks of trial and error getting this task accomplished. Great job!
December 30th, 2009 at 10:24 am
HELP! I have implemented every change that have been suggested and I still get the “SignatureDoesNotMatch”. Is it possible for someone to dump a complete script on the site that works?
Thanks
February 19th, 2010 at 7:20 am
You save my life !
March 1st, 2010 at 2:18 am
good
March 4th, 2010 at 6:38 am
[...] Loan. Post Match Reaction. Rafa Benitez. Rafa Hicks Gillette debacle. Reserves. Robbie Threlfall …Amazon AWS API REST Authentication for PHP 5 | I Love Bonnie.netSince Amazon decided all of their requests needed to be authenticated, developers have been [...]