Im writing a AWS API request to list users on IAM AWS service. And I'm receiving error message.
<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<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.
The Canonical String for this request should have been
'GET
/
Action=ListUsers&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMPILWMPQSH57DNA%2F20160621%2Fus-east-1%2Fiam%2Faws4_request&X-Amz-Date=20160621T142939Z&X-Amz-SignedHeaders=host
host:iam.amazonaws.com
host
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
The String-to-Sign should have been
'AWS4-HMAC-SHA256
20160621T142939Z
20160621/us-east-1/iam/aws4_request
c47728e278701ccaada8df76488c18449ada2f1b8aab6275a4bc0ada94af3ce2'
</Message>
</Error>
<RequestId>9672bcf2-37bc-11e6-8b2d-6151d0618c53</RequestId>
</ErrorResponse>
As you can see from my code bellow my canonical string is exactly the same like they wrote in error response but for some reason when im calculating hash my hex value is different that they write.
For example in this error response they wrote that hex value should be
`c47728e278701ccaada8df76488c18449ada2f1b8aab6275a4bc0ada94af3ce2`
and when I use functions
$hashedcanon = hash_hmac("sha256", $canonicalrequest, True);in my code im getting
`57fce72007b43c2621712b85e90fd38f0a1f2c7a3e84799fb9f477ed8546f86e`
Here is my code.
<?php
$AWSAccessKeyId = "<myaccesskey>";
$SecretAccessKey = "<mysecretkey>";
$timestamp = date('Ymd',time()).'T'.date('His',time()).'Z';
$date = date('Ymd',time());
$url = 'https://iam.amazonaws.com';
$method = 'GET';
$postfields['Action'] = 'ListUsers';
$postfields['Version'] = '2010-05-08';
$postfields["X-Amz-Algorithm"] = 'AWS4-HMAC-SHA256';
$postfields['X-Amz-Credential'] = $AWSAccessKeyId.'/'.$date.'/us-east-1/iam/aws4_request';
$postfields['X-Amz-Date'] = $timestamp;
$postfields['X-Amz-SignedHeaders'] = 'host';
$canonicalized_query = array();
foreach ($postfields as $param => $value) {
$param = str_replace("%7E", "~", rawurlencode($param));
$value = str_replace("%7E", "~", rawurlencode($value));
$canonicalized_query[] = $param . "=" . $value;
}
$canonicalized_query = implode("&", $canonicalized_query);
$canonicalrequest = $method."\n".
"/\n".
$canonicalized_query."\n".
"host:iam.amazonaws.com\n".
"\n".
"host\n".
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
$hashedcanon = hash_hmac("sha256", $canonicalrequest, True);
$string_to_sign = $postfields["X-Amz-Algorithm"]."\n".$timestamp."\n".$date."/us-east-1/iam/aws4_request\n".$hashedcanon;
$signingkey = hash_hmac("sha256",hash_hmac("sha256",hash_hmac("sha256",hash_hmac("sha256","AWS4".$SecretAccessKey,$date),"us-east-1"),"iam"),"aws4_request");
$signature = hash_hmac("sha256", $string_to_sign, $signingkey, True);
$postfields["X-Amz-Signature"] = $signature;
$canonicalized_query = array();
foreach ($postfields as $param => $value) {
$param = str_replace("%7E", "~", rawurlencode($param));
$value = str_replace("%7E", "~", rawurlencode($value));
$canonicalized_query[] = $param . "=" . $value;
}
$canonicalized_query = implode("&", $canonicalized_query);
$fullurl = $url.'/?'.$canonicalized_query;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $fullurl);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLINFO_HEADER_OUT, true); // enable tracking
$result = curl_exec($ch);
$headerSent = curl_getinfo($ch, CURLINFO_HEADER_OUT );
?>`
So in general I'm assuming I'm calculating wrong hex value of string to sign since my and theirs hex value of canonical string are not the same.
Also is interesting when I copy/paste my canonical string to http://hash.online-convert.com/sha256-generator I'm getting third hex value (not even my or theirs).
If anyone needs more info I'm willing to provide it or if anyone has working code for any API AWS if it can share it so I throw an eye and compare and hope I can find error.
Thanks
$hashedcanon = hash_hmac("sha256", $canonicalrequest, True);
Well, three issues...
hash_hmac() takes the key as the third argument, not a boolean, and
you aren't supposed to be calculating an HMAC digest here, so hash_hmac() isn't what you want, and
you want it in hex, not binary, so don't pass True.
You're looking for just hash().
$hashedcanon = hash("sha256", $canonicalrequest);
Note, I'm not saying hash_hmac() is not needed elsewhere -- just not on this line.
Hi guys I will post now fixed and working code just if someone else needs it.
<?php
$AWSAccessKeyId = "<my access key>";
$SecretAccessKey = "<my secret key>";
$timestamp = date('Ymd',time()).'T'.date('His',time()).'Z';
$date = date('Ymd',time());
$url = 'https://iam.amazonaws.com';
$method = 'GET';
$postfields['Action'] = 'ListUsers';
$postfields['Version'] = '2010-05-08';
$postfields["X-Amz-Algorithm"] = 'AWS4-HMAC-SHA256';
$postfields['X-Amz-Credential'] = $AWSAccessKeyId.'/'.$date.'/us-east-1/iam/aws4_request';
$postfields['X-Amz-Date'] = $timestamp;
$postfields['X-Amz-SignedHeaders'] = 'host';
$canonicalized_query = array();
foreach ($postfields as $param => $value) {
$param = str_replace("%7E", "~", rawurlencode($param));
$value = str_replace("%7E", "~", rawurlencode($value));
$canonicalized_query[] = $param . "=" . $value;
}
$canonicalized_query = implode("&", $canonicalized_query);
$canonicalrequest = $method."\n".
"/\n".
$canonicalized_query."\n".
"host:iam.amazonaws.com\n".
"\n".
"host\n".
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
$hasedcanon = hash("sha256",$canonicalrequest, false);
$credentialScope = $date."/us-east-1/iam/aws4_request";
$string_to_sign = "AWS4-HMAC-SHA256\n{$timestamp}\n{$credentialScope}\n{$hasedcanon}";
$dateKey = hash_hmac('sha256',$date,"AWS4{$SecretAccessKey}",true);
$regionKey = hash_hmac('sha256', "us-east-1", $dateKey, true);
$serviceKey = hash_hmac('sha256', "iam", $regionKey, true);
$key = hash_hmac('sha256','aws4_request',$serviceKey,true);
$signature = hash_hmac('sha256', $string_to_sign, $key);
$postfields["X-Amz-Signature"] = $signature;
$canonicalized_query = array();
foreach ($postfields as $param => $value) {
$param = str_replace("%7E", "~", rawurlencode($param));
$value = str_replace("%7E", "~", rawurlencode($value));
$canonicalized_query[] = $param . "=" . $value;
}
$canonicalized_query = implode("&", $canonicalized_query);
$fullurl = $url.'/?'.$canonicalized_query;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $fullurl);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLINFO_HEADER_OUT, true); // enable tracking
$xml = curl_exec($ch);
$headerSent = curl_getinfo($ch, CURLINFO_HEADER_OUT );
$doc = simplexml_load_string($xml);
echo '<pre>';
print_r($doc);
echo '</pre>';
?>
Related
This is what I have tried so far:
$t=time()+60;
$to_sign = "DELETE\n\n\n$t\n/myimage.jpg";
$signature = base64_encode( hash_hmac('sha1', utf8_encode( $to_sign ) , $auth['secretKey'], true) );
$url = "https://mybucket.s3-us-west-1.amazonaws.com/myimage.jpg?AWSAccessKeyId=MYKEYNUMBERXXX&Signature=$signature&Expires=$t";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST,'DELETE');
$result = curl_exec($ch);
Return error: SignatureDoesNotMatch
I'm trying to implement a simple function to delete images in AWS, without having to load the entire SDK.
The Canonicalized Resource in Signature Version 2 Query String authentication is /${bucket}/${key}.
$to_sign = "DELETE\n\n\n$t\n/myimage.jpg"; # incorrect
$to_sign = "DELETE\n\n\n$t\n/mybucket/myimage.jpg"; # correct
Note also that you may need to make some url-escaped substitutions in your signature:
+ becomes %2B
/ becomes %2F
= becomes %3D
Note also that because you are using Signature V2, this code will only work in regions where S3 was deployed before Signature V4 became standard in 2014.
deleteAWS4( "myBucket", "myimage.jpg");
function deleteAWS4( $bucket, $fileName ){
$auth['AccessKeyId'] = "XXXXXXXXXXXXXXXXXXXX";
$auth['secretKey'] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$region = "us-west-1";
$date = gmdate("D, d M Y H:i:s")." +0000";
$params = array();
$params['AWSAccessKeyId'] = $auth['AccessKeyId'];
$params['SignatureMethod'] = 'hmac-sha1';
$params['SignatureVersion'] = '2';
$params['Timestamp'] = $date;
uksort($params, 'strcmp'); $params_str = '';
foreach ($params as $key => $val){
$params_str .= rawurlencode($key).'='.rawurlencode($val).'&';
}
$params_str = str_replace('%7E', '~',$params_str); $params_str = substr($params_str, 0, -1);
$t=time()+60;
$to_sign = "DELETE\n\n\n$t\n/$fileName";
$signature = base64_encode( hash_hmac('sha1', utf8_encode( $to_sign ) , $auth['secretKey'], true) );
$url = "https://$bucket.s3-us-west-1.amazonaws.com/$fileName?AWSAccessKeyId={$params['AWSAccessKeyId']}&Signature=$signature&Expires=$t";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($ch, CURLOPT_HTTPHEADER, false);
//curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $to_sign);
//curl_setopt($ch, CURLINFO_HEADER_OUT, true);
// get the result
$result = curl_exec($ch); // raw result
$info = curl_getinfo($ch);
// turn the xml response into an array
$result = json_decode(json_encode(simplexml_load_string($result)),true);
echo $url.'<br>';
echo "<pre>"; print_r($result); echo "</pre>";
echo "<pre>"; print_r($info['request_header']); echo "</pre>";
}
When I run this code, I get a SignatureDoesNotMatch error
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.
<?php
$param = array();
$param['AWSAccessKeyId'] = 'AWSAccessKeyId';
$param['Action'] = 'ListOrders';
$param['MWSAuthToken'] = 'MWSAuthToken';
$param['MarketplaceId'] = 'A21TJRUUN4KGV';
$param['FulfillmentChannel.Channel.1'] = 'MFN';
$param['PaymentMethod.Method.1'] = 'COD';
$param['OrderStatus.Status.1'] = 'Pending';
$param['OrderStatus.Status.2'] = 'PendingAvailability';
$param['SellerId'] = 'AGNFZGZRZBUP1';
$param['SignatureMethod'] = 'HmacSHA256';
$param['SignatureVersion'] = '2';
$param['CreatedAfter'] = "2017-09-01T13:41:49Z";
$param['Timestamp'] = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
$param['Version'] = '2013-09-01';
$secret = 'secret key';
$url = array();
foreach ($param as $key => $val) {
$val = str_replace("%7E", "~", rawurlencode($val));
$url[] = $key . "=" . $val;
}
sort($url);
$arr = implode('&', $url);
$sign = 'POST' . "\n";
$sign .= 'mws.amazonservices.in' . "\n";
$sign .= '/Orders/2013-09-01' . "\n";
$sign .= $arr;
$signature = hash_hmac("sha256", $sign, $secret, true);
$signature = urlencode(base64_encode($signature));
$link = "https://mws.amazonservices.in/Orders/2013-09-01?";
$link .= $arr;
$link .= "&Signature=" . $signature;
echo($link); //for debugging - you can paste this into a browser and see if it loads.
$ch = curl_init($link);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/xml'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
echo "<pre>";
print_r($response);
print_r($info);
?>
Your signature calculation seems fine. However, you're calculating a signature based on a "POST", but then you're actually doing a GET. I'd guess that's what's throwing off the signature calculation on the other side.
How to mark an order shipped in Amazon MWS by using an XML feed - using the correct endpoint, service, version and headers?
This was the question, and it took me 3 days to manage it since amazon api is extremely chaotic, documentation is medieval, and most critical information like proper endpoint addresses to submit any particular information is a mystery to find. I was only able to create it by imitating a request at scratchpad, randomly testing sections, combining the examples for other requests for totally different sections and actions. Submitting XML feeds is the only way to manage your orders currently.
So, this may help someone.
I was able to make below simple code work :
$param = array();
$param['AWSAccessKeyId'] = 'YOURKEY';
$param['Action'] = 'SubmitFeed';
$param['Merchant'] = 'YOURMERCHANTID';
$param['FeedType'] = '_POST_ORDER_FULFILLMENT_DATA_';
$param['SignatureMethod'] = 'HmacSHA256';
$param['SignatureVersion'] = '2';
$param['Timestamp'] = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
$param['Version'] = '2009-01-01';
$param['MarketplaceId.Id.1'] = 'MARKETPLACEID';
$param['PurgeAndReplace'] = 'false';
$secret = 'YOURSECRETKEY';
$url = array();
foreach ($param as $key => $val) {
$key = str_replace("%7E", "~", rawurlencode($key));
$val = str_replace("%7E", "~", rawurlencode($val));
$url[] = "{$key}={$val}";
}
$amazon_feed='<?xml version="1.0"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>YOURMERCHANTID</MerchantIdentifier>
</Header>
<MessageType>OrderFulfillment</MessageType>
<Message>
<MessageID>1</MessageID>
<OrderFulfillment>
<AmazonOrderID>YOUROWNORDERIDHERE</AmazonOrderID>
<FulfillmentDate>'.$param['Timestamp'].'</FulfillmentDate>
<FulfillmentData>
<CarrierName>TRACKINGCONAMEHERE</CarrierName>
<ShippingMethod>TRACKINGCONAMEORMETHODHERE</ShippingMethod>
<ShipperTrackingNumber>TRACKINGNOHERE</ShipperTrackingNumber>
</FulfillmentData>
</OrderFulfillment>
</Message>
</AmazonEnvelope>';
sort($url);
$arr = implode('&', $url);
$sign = 'POST' . "\n";
$sign .= 'mws.amazonservices.com' . "\n";
$sign .= '/Feeds/'.$param['Version'].'' . "\n";
$sign .= $arr;
$signature = hash_hmac("sha256", $sign, $secret, true);
$httpHeader = array();
$httpHeader[] = 'Transfer-Encoding: chunked';
$httpHeader[] = 'Content-Type: application/xml';
$httpHeader[] = 'Content-MD5: ' . base64_encode(md5($amazon_feed, true));
$httpHeader[] = 'Expect:';
$httpHeader[] = 'Accept:';
$signature = urlencode(base64_encode($signature));
$link = "https://mws.amazonservices.com/Feeds/".$param['Version']."?";
$link .= $arr . "&Signature=" . $signature;
echo '<br>';
echo($link); //for debugging - you can paste this into a browser and see if it loads.
echo '<br>';
$ch = curl_init($link);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $amazon_feed);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
Gives the below output :
<?xml version="1.0"?><SubmitFeedResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/">SubmitFeedResult>FeedSubmissionInfo>FeedSubmissionId>XXXXXXXXXXXXXXXXXXX</FeedSubmissionId><FeedType>_POST_ORDER_FULFILLMENT_DATA_</FeedType><SubmittedDate>2014-09-09T00:36:29+00:00</SubmittedDate><FeedProcessingStatus>_SUBMITTED_</FeedProcessingStatus></FeedSubmissionInfo>/SubmitFeedResult>ResponseMetadata>RequestId>XXXXXXXXXXXXXXXXXXX</RequestId></ResponseMetadata></SubmitFeedResponse><?xml version="1.0"?><SubmitFeedResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/"><SubmitFeedResult><FeedSubmissionInfo><FeedSubmissionId>XXXXXXXXXXXXXXXXXXX</FeedSubmissionId>FeedType>_POST_ORDER_FULFILLMENT_DATA_</FeedType><SubmittedDate>2014-09-09T00:36:29+00:00</SubmittedDate>FeedProcessingStatus>_SUBMITTED_</FeedProcessingStatus></FeedSubmissionInfo>/SubmitFeedResult>ResponseMetadata>RequestId>XXXXXXXXXXXXXXXXXXX</RequestId></ResponseMetadata></SubmitFeedResponse>
The following function gives a validation error instead of the token:
failed to validate oAuth signature and token
function request_token()
{
// Set url
$url = $this->site.$this->request_token_path; // http://api.twitter.com/oauth/request_token
// Params to pass to twitter and create signature
$params['oauth_consumer_key'] = $this->consumerKey;
$params['oauth_token'] = '';
$params['oauth_nonce'] = SHA1(time());
$params['oauth_timestamp'] = time();
$params['oauth_signature_method'] = $this->signatureMethod; // HMAC-SHA1;
$params['oauth_version'] = $this->version; // 1.0
ksort($params);
//print "<pre>"; print_r($params); print "</pre>";
// Create Signature
$concatenatedParams = '';
foreach($params as $k => $v){
$concatenatedParams .= "{$k}={$v}&";
}
$concatenatedParams = substr($concatenatedParams,0,-1);
$signatureBaseString = "POST&".urlencode($url)."&".urlencode($concatenatedParams);
$params['oauth_signature'] = base64_encode(hash_hmac('SHA1', $signatureBaseString, $this->secret."&", TRUE));
// Do cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLINFO_HEADER_OUT, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
$exec = curl_exec ($ch);
$info = curl_getinfo($ch);
curl_close ($ch);
print $exec;
//print "<pre>"; print_r($info); print "</pre>";
}
Below is what Ive put together so far and it works :-)
class Twitauth
{
var $key = '';
var $secret = '';
var $request_token = "https://twitter.com/oauth/request_token";
function Twitauth($config)
{
$this->key = $config['key']; // consumer key from twitter
$this->secret = $config['secret']; // secret from twitter
}
function getRequestToken()
{
// Default params
$params = array(
"oauth_version" => "1.0",
"oauth_nonce" => time(),
"oauth_timestamp" => time(),
"oauth_consumer_key" => $this->key,
"oauth_signature_method" => "HMAC-SHA1"
);
// BUILD SIGNATURE
// encode params keys, values, join and then sort.
$keys = $this->_urlencode_rfc3986(array_keys($params));
$values = $this->_urlencode_rfc3986(array_values($params));
$params = array_combine($keys, $values);
uksort($params, 'strcmp');
// convert params to string
foreach ($params as $k => $v) {$pairs[] = $this->_urlencode_rfc3986($k).'='.$this->_urlencode_rfc3986($v);}
$concatenatedParams = implode('&', $pairs);
// form base string (first key)
$baseString= "GET&".$this->_urlencode_rfc3986($this->request_token)."&".$this->_urlencode_rfc3986($concatenatedParams);
// form secret (second key)
$secret = $this->_urlencode_rfc3986($this->secret)."&";
// make signature and append to params
$params['oauth_signature'] = $this->_urlencode_rfc3986(base64_encode(hash_hmac('sha1', $baseString, $secret, TRUE)));
// BUILD URL
// Resort
uksort($params, 'strcmp');
// convert params to string
foreach ($params as $k => $v) {$urlPairs[] = $k."=".$v;}
$concatenatedUrlParams = implode('&', $urlPairs);
// form url
$url = $this->request_token."?".$concatenatedUrlParams;
// Send to cURL
print $this->_http($url);
}
function _http($url, $post_data = null)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
if(isset($post_data))
{
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
$response = curl_exec($ch);
$this->http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$this->last_api_call = $url;
curl_close($ch);
return $response;
}
function _urlencode_rfc3986($input)
{
if (is_array($input)) {
return array_map(array('Twitauth', '_urlencode_rfc3986'), $input);
}
else if (is_scalar($input)) {
return str_replace('+',' ',str_replace('%7E', '~', rawurlencode($input)));
}
else{
return '';
}
}
}
not sure if your still looking into this, or if it will work for you, but i had a similar setup and had the same issue. I eventually found i was urlencoding one to many times.
Try commenting out this section:
$keys = $this->_urlencode_rfc3986(array_keys($params));
$values = $this->_urlencode_rfc3986(array_values($params));
$params = array_combine($keys, $values);
Worked for me, so maybe it will help.
I'll admit this isn't really an answer, but if you can, use the PECL OAuth package. Rasmus Lerdorf wrote a tutorial on how to use it and it got me around this same issue.
I faced same issue, what I was missing is passing header in to the curl request. As shown in this question, I was also sending the $header = array('Expect:'), which was the problem in my case. I started sending signature in header with other data as below and it solved the case for me.
$header = calculateHeader($parameters, 'https://api.twitter.com/oauth/request_token');
function calculateHeader(array $parameters, $url)
{
// redefine
$url = (string) $url;
// divide into parts
$parts = parse_url($url);
// init var
$chunks = array();
// process queries
foreach($parameters as $key => $value) $chunks[] = str_replace('%25', '%', urlencode_rfc3986($key) . '="' . urlencode_rfc3986($value) . '"');
// build return
$return = 'Authorization: OAuth realm="' . $parts['scheme'] . '://' . $parts['host'] . $parts['path'] . '", ';
$return .= implode(',', $chunks);
// prepend name and OAuth part
return $return;
}
function urlencode_rfc3986($value)
{
if(is_array($value)) return array_map('urlencode_rfc3986', $value);
else
{
$search = array('+', ' ', '%7E', '%');
$replace = array('%20', '%20', '~', '%25');
return str_replace($search, $replace, urlencode($value));
}
}
I'm trying to integrate Amazon Product API into my website and came across several posts that helped me construct the URL. Only problem is when I execute the code below I get the following error. Am I doing something wrong?
Internal Server Error The server
encountered an internal error or
misconfiguration and was unable to
complete your request. Please contact
the server administrator,
awsadmin#amazon.com and inform them of
the time the error occurred, and
anything you might have done that may
have caused the error. More
information about this error may be
available in the server error log.
$AWS_ACCESS_KEY_ID = "[myaccesskeyhere]";
$AWS_SECRET_ACCESS_KEY = "[mysecretkeyhere]";
$base_url = "http://ecs.amazonaws.com/onca/xml?";
$url_params = array('Operation'=>"ItemSearch",'Service'=>"AWSECommerceService",
'AWSAccessKeyId'=>$AWS_ACCESS_KEY_ID,'AssociateTag'=>"yourtag-10",
'Version'=>"2006-09-11",'Availability'=>"Available",'Condition'=>"All",
'ItemPage'=>"1",'ResponseGroup'=>"Images,ItemAttributes,EditorialReview",
'Keywords'=>"Amazon");
// Add the Timestamp
$url_params['Timestamp'] = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($url_params) as $key)
$url_parts[] = $key."=".$url_params[$key];
sort($url_parts);
// Construct the string to sign
$string_to_sign = "GET\necs.amazonaws.com\n/onca/xml\n".implode("&",$url_parts);
$string_to_sign = str_replace('+','%20',$string_to_sign);
$string_to_sign = str_replace(':','%3A',$string_to_sign);
$string_to_sign = str_replace(';',urlencode(';'),$string_to_sign);
// Sign the request
$signature = hash_hmac("sha256",$string_to_sign,$AWS_SECRET_ACCESS_KEY,TRUE);
// Base64 encode the signature and make it URL safe
$signature = base64_encode($signature);
$signature = str_replace('+','%2B',$signature);
$signature = str_replace('=','%3D',$signature);
$url_string = implode("&",$url_parts);
$url = $base_url.$url_string."&Signature=".$signature;
print $url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$xml_response = curl_exec($ch);
echo $xml_response;
EDIT
THE ABOVE CODE NOW WORKS...I WAS MISSING A "?" after the BASE URL
$AWS_ACCESS_KEY_ID = "[myaccesskeyhere]";
$AWS_SECRET_ACCESS_KEY = "[mysecretkeyhere]";
$base_url = "http://ecs.amazonaws.com/onca/xml?";
$url_params = array('Operation'=>"ItemSearch",'Service'=>"AWSECommerceService",
'AWSAccessKeyId'=>$AWS_ACCESS_KEY_ID,'AssociateTag'=>"yourtag-10",
'Version'=>"2006-09-11",'Availability'=>"Available",'Condition'=>"All",
'ItemPage'=>"1",'ResponseGroup'=>"Images,ItemAttributes,EditorialReview",
'Keywords'=>"Amazon");
// Add the Timestamp
$url_params['Timestamp'] = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($url_params) as $key)
$url_parts[] = $key."=".$url_params[$key];
sort($url_parts);
// Construct the string to sign
$string_to_sign = "GET\necs.amazonaws.com\n/onca/xml\n".implode("&",$url_parts);
$string_to_sign = str_replace('+','%20',$string_to_sign);
$string_to_sign = str_replace(':','%3A',$string_to_sign);
$string_to_sign = str_replace(';',urlencode(';'),$string_to_sign);
// Sign the request
$signature = hash_hmac("sha256",$string_to_sign,$AWS_SECRET_ACCESS_KEY,TRUE);
// Base64 encode the signature and make it URL safe
$signature = base64_encode($signature);
$signature = str_replace('+','%2B',$signature);
$signature = str_replace('=','%3D',$signature);
$url_string = implode("&",$url_parts);
$url = $base_url.$url_string."&Signature=".$signature;
print $url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$xml_response = curl_exec($ch);
echo $xml_response;
For anyone that was wondering. You can then navigate the xml_response using simplexml!
Hope this helps someone out there :)
Use Bellow code for the Amazon Product API:
<?php
//Enter your IDs
define("Access_Key_ID", "[Your Access Key ID]");
define("Associate_tag", "[Your Associate Tag ID]");
//Set up the operation in the request
function ItemSearch($SearchIndex, $Keywords){
//Set the values for some of the parameters
$Operation = "ItemSearch";
$Version = "2013-08-01";
$ResponseGroup = "ItemAttributes,Offers";
//User interface provides values
//for $SearchIndex and $Keywords
//Define the request
$request=
"http://webservices.amazon.com/onca/xml"
. "?Service=AWSECommerceService"
. "&AssociateTag=" . Associate_tag
. "&AWSAccessKeyId=" . Access_Key_ID
. "&Operation=" . $Operation
. "&Version=" . $Version
. "&SearchIndex=" . $SearchIndex
. "&Keywords=" . $Keywords
. "&Signature=" . [Request Signature]
. "&ResponseGroup=" . $ResponseGroup;
//Catch the response in the $response object
$response = file_get_contents($request);
$parsed_xml = simplexml_load_string($response);
printSearchResults($parsed_xml, $SearchIndex);
}
?>