Home     Articles & Projects     Products & Web Services

Amazon API Request Authentication

I have Amazon authentication working with the following code - a couple of people are testing it out and once I know it's OK i'll update the Amazon page on Price Tapestry. This method requires PHP4+mhash, PHP5+mhash or PHP5 >= 5.1.2.

The commented steps relate to the Amazon AWS documentation on this page.

<?php
 
function amazonEncode($text)
  {
   
$encodedText = "";
   
$j = strlen($text);
    for(
$i=0;$i<$j;$i++)
    {
     
$c = substr($text,$i,1);
      if (!
preg_match("/[A-Za-z0-9\-_.~]/",$c))
      {
       
$encodedText .= sprintf("%%%02X",ord($c));
      }
      else
      {
       
$encodedText .= $c;
      }
    }
    return
$encodedText;
  }

  function
amazonSign($url,$secretAccessKey)
  {
   
// 0. Append Timestamp parameter
   
$url .= "&Timestamp=".gmdate("Y-m-d\TH:i:s\Z");

   
// 1a. Sort the UTF-8 query string components by parameter name
   
$urlParts = parse_url($url);
   
parse_str($urlParts["query"],$queryVars);
   
ksort($queryVars);

   
// 1b. URL encode the parameter name and values
   
$encodedVars = array();
    foreach(
$queryVars as $key => $value)
    {
     
$encodedVars[amazonEncode($key)] = amazonEncode($value);
    }

   
// 1c. 1d. Reconstruct encoded query
   
$encodedQueryVars = array();
    foreach(
$encodedVars as $key => $value)
    {
     
$encodedQueryVars[] = $key."=".$value;
    }
   
$encodedQuery = implode("&",$encodedQueryVars);

   
// 2. Create the string to sign
   
$stringToSign  = "GET";
   
$stringToSign .= "\n".strtolower($urlParts["host"]);
   
$stringToSign .= "\n".$urlParts["path"];
   
$stringToSign .= "\n".$encodedQuery;

   
// 3. Calculate an RFC 2104-compliant HMAC with the string you just created,
    //    your Secret Access Key as the key, and SHA256 as the hash algorithm.
   
if (function_exists("hash_hmac"))
    {
     
$hmac = hash_hmac("sha256",$stringToSign,$secretAccessKey,TRUE);
    }
    elseif(
function_exists("mhash"))
    {
     
$hmac = mhash(MHASH_SHA256,$stringToSign,$secretAccessKey);
    }
    else
    {
      die(
"No hash function available!");
    }

   
// 4. Convert the resulting value to base64
   
$hmacBase64 = base64_encode($hmac);

   
// 5. Use the resulting value as the value of the Signature request parameter
    // (URL encoded as per step 1b)
   
$url .= "&Signature=".amazonEncode($hmacBase64);

    return
$url;
  }
?>

To use, construct $url without the Timestamp parameter, and then sign using:

$url = amazonSign($url,"YOUR_SECRET_ACCESS_KEY");

Note: If you have been using the REST API for some time, you may be using the old SubscriptionId parameter. Authenticated URLs require the newer AWSAccessKeyId parameter, which together with your secret access key can be found on this page.