PHP Code to Sign any Amazon API Requests

Posted on June 30th, 2009 in General,Linux System Administration,PHP,Programming by Brandon

Starting next month, any requests to the Amazon Product Advertising API need to be cryptographically signed. Amazon has given about three months notice and the deadline is quickly approaching. I use the Amazon web services on several sites and came up a fairly generic way to convert an existing URL to a signed URL. I’ve tested with several sites and a variety of functions, and this is working well for me so far:

function signAmazonUrl($url, $secret_key)
{
    $original_url = $url;

    // Decode anything already encoded
    $url = urldecode($url);

    // Parse the URL into $urlparts
    $urlparts       = parse_url($url);

    // Build $params with each name/value pair
    foreach (split('&', $urlparts['query']) as $part) {
        if (strpos($part, '=')) {
            list($name, $value) = split('=', $part, 2);
        } else {
            $name = $part;
            $value = '';
        }
        $params[$name] = $value;
    }

    // Include a timestamp if none was provided
    if (empty($params['Timestamp'])) {
        $params['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
    }

    // Sort the array by key
    ksort($params);

    // Build the canonical query string
    $canonical       = '';
    foreach ($params as $key => $val) {
        $canonical  .= "$key=".rawurlencode(utf8_encode($val))."&";
    }
    // Remove the trailing ampersand
    $canonical       = preg_replace("/&$/", '', $canonical);

    // Some common replacements and ones that Amazon specifically mentions
    $canonical       = str_replace(array(' ', '+', ',', ';'), array('%20', '%20', urlencode(','), urlencode(':')), $canonical);

    // Build the sign
    $string_to_sign             = "GET\n{$urlparts['host']}\n{$urlparts['path']}\n$canonical";
    // Calculate our actual signature and base64 encode it
    $signature            = base64_encode(hash_hmac('sha256', $string_to_sign, $secret_key, true));

    // Finally re-build the URL with the proper string and include the Signature
    $url = "{$urlparts['scheme']}://{$urlparts['host']}{$urlparts['path']}?$canonical&Signature=".rawurlencode($signature);
    return $url;
}

To use it, just wrap your Amazon URL with the signAmazonUrl() function and pass it your original string and secret key as arguments. As an example:

$xml = file_get_contents('http://webservices.amazon.com/onca/xml?some-parameters');

becomes

$xml = file_get_contents(signAmazonUrl('http://webservices.amazon.com/onca/xml?some-parameters', $secret_key));

Like most all of the variations of this, it does require the hash functions be installed to use the hash_hmac() function. That function is generally available in PHP 5.1+. Older versions will need to install it with Pecl. I tried using a couple of versions that try to create the Hash in pure PHP code, but none worked and installing it via Pecl was pretty simple.

(Note that I’ve slightly revised this code a couple of times to fix small issues that have been noticed)

19 Responses to 'PHP Code to Sign any Amazon API Requests'

Subscribe to comments with RSS or TrackBack to 'PHP Code to Sign any Amazon API Requests'.

  1. Peter Jones said,

    on August 10th, 2009 at 2:55 pm

    Doesn’t work mate. Just “The request signature we calculated does not match the signature you provided” notice.

    Good idea though

  2. Brandon said,

    on August 10th, 2009 at 3:02 pm

    The code works for me on quite a few sites. If you still need a solution, try out http://www.amazonapisigning.com/

  3. Morph said,

    on August 18th, 2009 at 6:03 am

    Great solution – Thank you!
    Works great for all sites – saves a lot of work!

  4. Zaid said,

    on August 19th, 2009 at 12:19 pm

    YOU ROCK! Works perfectly.


  5. on August 27th, 2009 at 6:56 am

    Many thanks Brandon – easy and painless to implement.

  6. Mammut said,

    on August 31st, 2009 at 3:08 pm

    Cool implementation, saves me a lot of time reading Amazon API manuals. By the way, it does not work with any special characters like ÄÖ etc. in the url. As soon as i have a proper implementation for this i will post it here.


  7. on September 25th, 2009 at 1:47 pm

    Cool man, this snippet of code saved me a lot of time.

    Thanks from me, my time and my finances :P

  8. Marina said,

    on October 1st, 2009 at 1:43 am

    hey
    I am new to php…
    can any one tell me what we have to pass as a url in this function….it will be great if some one show me the argument as a sample being passed to this function.
    thnx in advance….

  9. Brandon said,

    on October 1st, 2009 at 4:10 pm

    @Mariana,

    You should call the function and pass it any unsigned URL. IE:
    http://free.apisigning.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=YOUR_KEY_HERE&Operation=ItemLookup&ItemId=0596006810&ResponseGroup=ItemAttributes

  10. Sandro said,

    on October 14th, 2009 at 5:50 pm

    hello, this thing is driving me crazy I don’t get why Amazon required to make such a complicated request. I tried your script but it just keeps saying the signature is wrong.

    $url = signAmazonUrl(“http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAJF2HBLPGGOIGB4HA&Operation=ItemLookup&ItemId=B0027FFT70&ResponseGroup=Large”);

    this is the request, i added my secret key by defying it in your script.

    It doesn’t work for me.

  11. Brandon said,

    on October 14th, 2009 at 7:09 pm

    @Sandro

    In your request, your ampersands have been URL Encoded, and they shouldn’t be when passing to that function.

    Also, you can use the free signing service that I offer at APISigning.com where all you have to do is change the hostname in your original request to make it work.

    Thanks,
    Brandon Checketts

  12. Sandro said,

    on October 14th, 2009 at 8:37 pm

    Thank you now it works just fine.


  13. on October 28th, 2009 at 9:38 am

    I tried this script and it didn’t work for me so I made simplified version for my needs.

    For those who cannot get this script to work, feel look at my code as it may help you – http://www.jamiebicknell.com/blog/27-Oct-09/View-Your-Amazon-Wishlist-via-PHP

    Thank you

  14. Brandon said,

    on October 28th, 2009 at 11:33 am

    @Jaime,

    That looks like a good simplification. I’ve intentionally made mine take some extra steps so that the code is more self-explanatory and easy to follow. Yours will also throw a few PHP Notices which I generally try to avoid.


  15. on November 1st, 2009 at 10:48 am

    @Brandon I can’t see/find any PHP Notices?

  16. Brandon said,

    on November 1st, 2009 at 9:08 pm

    @Jamie,

    Actually, in looking at it in more detail, the code that I originally thought would cause the Notice, it looks like I was wrong. Code looks pretty good to me.


  17. on November 3rd, 2009 at 8:31 am

    Cheers dude

  18. john said,

    on November 26th, 2009 at 1:41 pm

    Working perfectly .. thank you so much.

  19. Deepak said,

    on December 19th, 2009 at 2:57 pm

    Thanks Brandon, You saved me a ton of work!!! Works beautifully. First I was getting a Signature not matched error, but later I found my URL was not properly encoded.

Post a comment

Please copy the string 6YdBBO to the field below: