最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to properly sign a GET request to Amazon's ItemLookup using client-side JavaScript only? - Stack Overflow

programmeradmin1浏览0评论

Here's what I have so far:

function sha256(stringToSign, secretKey) {
  return CryptoJS.HmacSHA256(stringToSign, secretKey);
} 

function getAmazonItemInfo(barcode) {
  
  var parameters = 
    "Service=AWSECommerceService&"
    + "AWSAccessKeyId=" + appSettings.amazon.accessKey + "&"
    + "Operation=ItemLookup&"
    + "ItemId=" + barcode
    + "&Timestamp=" + Date.now().toString();

  var stringToSign =
    "GET\n"
    + "webservices.amazon\n"
    + "/onca/xml\n"
    + parameters;

  var signature = "&Signature=" + encodeURIComponent(sha256(stringToSign, appSettings.amazon.secretKey));

  var amazonUrl =  
    "?"
    + parameters
    + signature;

  // perform a GET request with amazonUrl and do other stuff

}

When executed as an HTTP GET request, the value of amazonUrl in the above code results in the following response from Amazon:

<?xml version="1.0"?> 
  <ItemLookupErrorResponse xmlns="/">
    <Error>
      <Code>SignatureDoesNotMatch</Code>
      <Message>
        The request signature we calculated does not match the signature you provided. 
        Check your AWS Secret Access Key and signing method. Consult the service 
        documentation for details.
      </Message>
   </Error>
   <RequestId>[REMOVED]</RequestId>
  </ItemLookupErrorResponse>

Useful links:

  • ItemLookup - Product Advertising API Amazon Documentation

  • Example REST Requests

  • AWS Authentication Process

  • CryptoJS

Here's what I have so far:

function sha256(stringToSign, secretKey) {
  return CryptoJS.HmacSHA256(stringToSign, secretKey);
} 

function getAmazonItemInfo(barcode) {
  
  var parameters = 
    "Service=AWSECommerceService&"
    + "AWSAccessKeyId=" + appSettings.amazon.accessKey + "&"
    + "Operation=ItemLookup&"
    + "ItemId=" + barcode
    + "&Timestamp=" + Date.now().toString();

  var stringToSign =
    "GET\n"
    + "webservices.amazon.\n"
    + "/onca/xml\n"
    + parameters;

  var signature = "&Signature=" + encodeURIComponent(sha256(stringToSign, appSettings.amazon.secretKey));

  var amazonUrl =  
    "http://webservices.amazon./onca/xml?"
    + parameters
    + signature;

  // perform a GET request with amazonUrl and do other stuff

}

When executed as an HTTP GET request, the value of amazonUrl in the above code results in the following response from Amazon:

<?xml version="1.0"?> 
  <ItemLookupErrorResponse xmlns="http://ecs.amazonaws./doc/2005-10-05/">
    <Error>
      <Code>SignatureDoesNotMatch</Code>
      <Message>
        The request signature we calculated does not match the signature you provided. 
        Check your AWS Secret Access Key and signing method. Consult the service 
        documentation for details.
      </Message>
   </Error>
   <RequestId>[REMOVED]</RequestId>
  </ItemLookupErrorResponse>

Useful links:

  • ItemLookup - Product Advertising API Amazon Documentation

  • Example REST Requests

  • AWS Authentication Process

  • CryptoJS

Share Improve this question edited Feb 16, 2023 at 21:55 starball 54.2k36 gold badges238 silver badges936 bronze badges asked Mar 23, 2014 at 5:55 user396070user396070 4
  • This might also help: github./livelycode/aws-lib/blob/master/examples/prod-adv.js – user396070 Commented Mar 23, 2014 at 6:21
  • 2 Your missing a Base64 Encode before you encodeURI I believe. – David Commented Mar 23, 2014 at 6:26
  • 1 @David I didn't read anywhere in the Amazon documentation about Base64 encoding, but I see that your PHP snippet does it. How did you figure out that it needed to be Base64 encoded? – user396070 Commented Mar 23, 2014 at 7:30
  • 1 I forgot where I read it, I coded my library a few years ago and have been using it for everything so I don't really worry about any of the low level stuff anymore. – David Commented Mar 23, 2014 at 7:41
Add a ment  | 

3 Answers 3

Reset to default 4

I hacked around with your code and I got it working.

function sha256(stringToSign, secretKey) {
  var hex = CryptoJS.HmacSHA256(stringToSign, secretKey);
  return hex.toString(CryptoJS.enc.Base64);
} 

function timestamp() {
    var date = new Date();
    var y = date.getUTCFullYear().toString();
    var m = (date.getUTCMonth() + 1).toString();
    var d = date.getUTCDate().toString();
    var h = date.getUTCHours().toString();
    var min = date.getUTCMinutes().toString();
    var s = date.getUTCSeconds().toString();

    if(m.length < 2) { m = "0" + m; }
    if(d.length < 2) { d = "0" + d; }
    if(h.length < 2) { h = "0" + h; }
    if(min.length < 2) { min = "0" + min; }
    if(s.length < 2) { s = "0" + s}

    var date = y + "-" + m + "-" + d;
    var time = h + ":" + min + ":" + s;
    return date + "T" + time + "Z";
}

function getAmazonItemInfo(barcode) {
    var PrivateKey = "";
    var PublicKey = "";
    var AssociateTag = "";

    var parameters = [];
    parameters.push("AWSAccessKeyId=" + PublicKey);
    parameters.push("ItemId=" + barcode);
    parameters.push("Operation=ItemLookup");
    parameters.push("Service=AWSECommerceService");
    parameters.push("Timestamp=" + encodeURIComponent(timestamp()));
    parameters.push("Version=2011-08-01");
parameters.push("AssociateTag=" + AssociateTag);

    parameters.sort();
    var paramString = parameters.join('&');

    var signingKey = "GET\n" + "webservices.amazon.\n" + "/onca/xml\n" + paramString

    var signature = sha256(signingKey,PrivateKey);
        signature = encodeURIComponent(signature);

    var amazonUrl =  "http://webservices.amazon./onca/xml?" + paramString + "&Signature=" + signature;
    console.log(amazonUrl);
}

The Header of the Javascript I used for some reference.

<script src="hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode./svn/tags/3.0.2/build/ponents/enc-base64-min.js"></script>
<script src="amazon.js"></script>

You will need to modify parts of it because I changed some parameters around and don't reference your "app" object.

For what I did to fix it (from what I can recall).

  1. The parameters have to be alphabetical. I placed them in an array and then sort them. I follow this up by a join with the ampersand.

  2. I modified the sha256 function to return the base64 of the RAW sha256. Before it was returning the hexbits in lowercase, which isn't correct.

  3. I was going to add a base64 before encoding, but the sha256 now handles all of the signing.

  4. The date format was incorrect. It was returning a epoch timestamp instead of a string timestamp. I hacked together a simple timestamp option.

    This code requires you to include the Base64 Library for CryptoJS also.

Use this Node.js library for AWS. It even includes an example specifically for the Product Advertising API.

Building on David's great answer, I made some tweaks. The solution below uses moment.js and crytpo-js, and can be used to search for items by keyword. I used the amazon scratch-pad to help build the target call. A couple of things I noticed:

  • The scratch-pad needs to use the same location as your associates account, "." ".co.uk", etc.
  • The end point you call to needs to be the same country as your associates account.
  • The time-stamp you use needs to match the local time in the country your associates account is registered.

const getAmazonItemInfo = (keywords) => {

  let date = moment().startOf().add(-9, 'hours').format("YYYY-MM-DDThh:mm:ss.000") + 'Z'
  let SecretKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AccessKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AssociateTag = "FOUND_IN_AFFILATES_ACCOUNT";
  let parameters = [];
  let url = 'webservices.amazon.co.uk' // UK account
  //let url = 'webservices.amazon.'// US account

  parameters.push("AWSAccessKeyId=" + AccessKey);
  parameters.push("Keywords=" + keywords);
  parameters.push("Operation=ItemSearch");
  parameters.push("SearchIndex=All");
  parameters.push("ResponseGroup=" + encodeURIComponent('Images,ItemAttributes,Offers'));
  parameters.push("Service=AWSECommerceService");
  parameters.push("Timestamp=" + encodeURIComponent(date));
  parameters.push("AssociateTag=" + AssociateTag);
  parameters.sort();

  let paramString = parameters.join('&');
  let string_to_sign = "GET\n" + url + "\n" + "/onca/xml\n" + paramString

  let signature = CryptoJS.HmacSHA256(string_to_sign, SecretKey);
  signature = CryptoJS.enc.Base64.stringify(signature);

  let amazonUrl = "http://" + url + "/onca/xml?" + paramString + "&Signature=" + signature;
  return amazonUrl;
}

let keywords = 'iphone'
console.log(getAmazonItemInfo(keywords))

发布评论

评论列表(0)

  1. 暂无评论