Amazon® AWS HMAC signed request using PHP
The Amazon® Product Advertising API can be used to access Amazon's data for advertising purpose. By August 15, 2009, all calls to the API must be signed to authenticate the request. I have written a simple function in PHP that lets you make authenticated requests with only a few lines of code.
Source Code
- <?php
- function aws_signed_request($region, $params, $public_key, $private_key)
- {
- /*
- Copyright (c) 2009 Ulrich Mierendorff
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
- /*
- Parameters:
- $region - the Amazon(r) region (ca,com,co.uk,de,fr,jp)
- $params - an array of parameters, eg. array("Operation"=>"ItemLookup",
- "ItemId"=>"B000X9FLKM", "ResponseGroup"=>"Small")
- $public_key - your "Access Key ID"
- $private_key - your "Secret Access Key"
- */
- // some paramters
- $method = "GET";
- $host = "ecs.amazonaws.".$region;
- $uri = "/onca/xml";
- // additional parameters
- $params["Service"] = "AWSECommerceService";
- $params["AWSAccessKeyId"] = $public_key;
- // GMT timestamp
- $params["Timestamp"] = gmdate("Y-m-d\TH:i:s\Z");
- // API version
- $params["Version"] = "2009-03-31";
- // sort the parameters
- ksort($params);
- // create the canonicalized query
- $canonicalized_query = array();
- foreach ($params as $param=>$value)
- {
- $param = str_replace("%7E", "~", rawurlencode($param));
- $value = str_replace("%7E", "~", rawurlencode($value));
- $canonicalized_query[] = $param."=".$value;
- }
- $canonicalized_query = implode("&", $canonicalized_query);
- // create the string to sign
- $string_to_sign = $method."\n".$host."\n".$uri."\n".$canonicalized_query;
- // calculate HMAC with SHA256 and base64-encoding
- $signature = base64_encode(hash_hmac("sha256", $string_to_sign, $private_key, True));
- // encode the signature for the request
- $signature = str_replace("%7E", "~", rawurlencode($signature));
- // create request
- $request = "http://".$host.$uri."?".$canonicalized_query."&Signature=".$signature;
- // do request
- $response = @file_get_contents($request);
- if ($response === False)
- {
- return False;
- }
- else
- {
- // parse XML
- $pxml = simplexml_load_string($response);
- if ($pxml === False)
- {
- return False; // no xml
- }
- else
- {
- return $pxml;
- }
- }
- }
- ?>
Download
Documentation
After downloading and extracting aws_signed_request.zip you can include aws_signed_request.php in your PHP scripts. The function contained in this file takes four parameters. $region is the Amazon region (for example "com" or "fr"). $params is an array of parameters with the parameter names as keys. $public_key and $private_key are your keys you have got from Amazon.
Here is an example (you have to replace $public_key and $private_key with your own identifiers)
- include("aws_signed_request.php");
- $public_key = "xxxxxx";
- $private_key = "xxxxxx";
- $pxml = aws_signed_request("com", array("Operation"=>"ItemLookup","ItemId"=>"B000X9FLKM","ResponseGroup"=>"Small"), $public_key, $private_key);
- if ($pxml === False)
- {
- echo "Did not work.\n";
- }
- else
- {
- if (isset($pxml->Items->Item->ItemAttributes->Title))
- {
- echo $pxml->Items->Item->ItemAttributes->Title, "\n";
- }
- else
- {
- echo "Could not find item.\n";
- }
- }
The result would be something like this
- The Lord of the Rings: The Motion Picture Trilogy (Theatrical Editions) [Blu-ray]
That's it. Feel free to comment if you think something could be improved.