Amazon API signature - php

I've been attempting to create a signature to amazons API service, however I keep getting
SignatureDoesNotMatch on all my attempts and I honestly can't see what I've done wrong. This is my code;
<?php
require_once('.config.inc.php');
$sercretAccessKey = "XXXXX";
$site = $_GET['site'];
$serviceUrl = "";
$signatureUrl = "";
//UK marketplace
$marketplaceIdNumber = "A1F83G8C2ARO7P";
date_default_timezone_set('America/Phoenix');
$serviceUrl = "https://mws.amazonservices.de/Orders/2013-09-01";
$signatureUrl = "mws.amazonservices.de";
// Get total from 31 days ago
$t1 = date("c", time()-31*24*60*60);
$AccessKey = "AWSAccessKeyId=" . urlencode("xxxxx");
$action = "&Action=" . urlencode("ListOrders");
$fulfillmentChannel = "&FulfillmentChannel.Channel.1=" . urlencode("MFN");
$updateAfter = "&LastUpdatedAfter=" . urlencode($t1);
$marketplaceId = "&MarketplaceId.Id.1=" . urlencode($marketplaceIdNumber);
$orderStatus1 = "&OrderStatus.Status.1=" . urlencode("Unshipped");
$orderStatus2 = "&OrderStatus.Status.2=" . urlencode("PartiallyShipped");
$sellerID = "&SellerId=" . urlencode("xxxx");
$signatureMethod = "&SignatureMethod=" . urlencode("HmacSHA256");
$signatureVersion = "&SignatureVersion=" . urlencode("2");
$timeStamp = "&Timestamp=" . urlencode(date("c"));
$version = "&Version=" . urlencode("2013-09-01");
$stringToSignature =
"POST\n" .
"$signatureUrl\n" .
"/Orders/2013-09-01\n" .
$AccessKey . $action . $fulfillmentChannel . $updateAfter . $marketplaceId . $orderStatus1 . $orderStatus2 . $sellerID . $signatureMethod . $signatureVersion . $timeStamp . $version;
$signatureCode = hash_hmac('sha256', $stringToSignature, $sercretAccessKey, true);
$signatureCodeBaseEncoded = base64_encode($signatureCode);
$signatureCodeEncoded = urlencode($signatureCodeBaseEncoded);
$signature = "&Signature=" . $signatureCodeEncoded;
$targetURL = $serviceUrl . "?" . $AccessKey . $action . $fulfillmentChannel . $updateAfter . $marketplaceId . $orderStatus1 . $orderStatus2 . $sellerID . $signatureMethod . $signatureVersion . $timeStamp . $version . $signature;
// create context
$context = array('header' => 'Content-Type: text/xml');
$ci = curl_init($targetURL);
curl_setopt($ci, CURLOPT_POST, 1);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ci, CURLOPT_POSTFIELDS,"xmlRequest");
$result = curl_exec($ci);
echo $result;
?>
In theory this should work. I've followed their tutorials and followed what their scratchpad told me. So my request should be in canonical format.
Any help would be great!
I should probably note that the only way I can get any response from amazon is through the german serviceurl.

Related

Using Amazon SQS Rest API in PHP: AccessDenied error

I have an app running on PHP 5.3 and it needs to use Amazon SQS. I must use AWS REST API directly, and not AWS PHP SDK, since the recent SDKs do not run on PHP 5.3, and the older SDKs do not support the current API parameters of SQS.
I am trying a simple SendMessage API call with a minimal script, which is attached below. It always shows the following error, and no SQS message is sent, of course.
Code: 'AccessDenied' and Message: 'Access to the resource https://sqs.us-east-1.amazonaws.com/999999999999/test1.fifo is denied'.
1) I have primarily followed these docs, and a sample Python script available there, for writing this code.
a) https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-working-with-apis.html
b) https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
2) I have checked these critical variables in my code several times and they are correct: endpoint, host, region, key, secrete, uri etc.
3) I have used those same variables in AWS PHP SDK for SQS on another computer running a recent version of PHP, and that has worked successfully.
UPDATE >> 4) The access id and secrete key used in the code belong to an IAM user who has full AWS Administrative privilege.
/////// COMPLETE CODE //////////////
$method = 'POST';
$service = 'sqs';
$endpoint = 'https://sqs.us-east-1.amazonaws.com/999999999999/test1.fifo';
$host = 'sqs.us-east-1.amazonaws.com';
$region = 'us-east-1';
$key = 'XXXXXXXXXXXXXXXXX';
$secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$content_type = 'application/x-www-form-urlencoded';
$amz_date = gmdate('Ymd').'T'.gmdate('His').'Z';
$date_stamp = gmdate('Ymd');
//post fields for SendMessage()
$message_group_id = 1;
$message_deduplication_id = $amz_date;
$message = 'An awesome message sent at: ' . $amz_date;
$post_data = 'Action=SendMessage&Version=2012-11-05&MessageGroupId='. urlencode($message_group_id) .'&MessageDeduplicationId=' . urlencode($message_deduplication_id) . '&MessageBody=' . urlencode($message);
//CANONICAL REQUEST
$canonical_uri = '/999999999999/test1.fifo';
$canonical_querystring = '';
$canonical_headers = 'content-type:' . $content_type . '\n' . 'host:' . $host . '\n' . 'x-amz-date:' . $amz_date . '\n';
$signed_headers = 'content-type;host;x-amz-date';
$payload_hash = hash('sha256', utf8_encode($post_data));
$canonical_request = $method . '\n' . $canonical_uri . '\n' . $canonical_querystring . '\n' . $canonical_headers . '\n' . $signed_headers . '\n' . $payload_hash;
//STRING TO SIGN
$algorithm_name = 'AWS4-HMAC-SHA256';
$algorithm = 'sha256';
$credential_scope = $date_stamp . '/' . $region . '/' . $service . '/' . 'aws4_request';
$string_to_sign = $algorithm_name . '\n' . $amz_date . '\n' . $credential_scope . '\n' . hash('sha256', utf8_encode($canonical_request));
//SIGNATURE
$dateKey = hash_hmac($algorithm, $date_stamp, utf8_encode('AWS4' . $secret), true);
$dateRegionKey = hash_hmac($algorithm, $region, $dateKey, true);
$dateRegionServiceKey = hash_hmac($algorithm, $service, $dateRegionKey, true);
$signingKey = hash_hmac($algorithm, 'aws4_request', $dateRegionServiceKey, true);
$signature = hash_hmac('sha256', $string_to_sign, $signingKey, false);
//REQUEST
$authorization_header = $algorithm_name . ' ' . 'Credential=' . $key . '/' . $credential_scope . ', ' . 'SignedHeaders=' . $signed_headers . ', ' . 'Signature=' . $signature;
$http_header = array(
'Content-Type' => $content_type,
'X-Amz-Date' => $amz_date,
'Authorization' => $authorization_header
);
//SEND
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $endpoint);
curl_setopt($curl, CURLOPT_HTTPHEADER, $http_header);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
if ($response = curl_exec($curl)) {
$response_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
echo '<br><br><br>Response code: ' . $response_code;
$xml = simplexml_load_string($response);
print_r($xml);
}
else {
$error_code = curl_errno($curl);
$error_message = curl_error($curl);
echo '<br><br><br>Error: ' . $error_code . ' => ' . $error_message;
}
#curl_close($curl);
Any observation/suggestion as to what is causing the error will be appreciated.

Generating OAuth 1 Signature in PHP

I'm trying to connect to the LivePerson Engagement History API and I'm running into an issue that I believe is related to the signature being generated.
First off, the API already provides the necessary consumer key, consumer secret, access token, and token secret. So I don't have to go through the process of retrieving those. In order to access their API I just have to provide the auth header. I've mocked everything up using Postman and it all works correctly. The issue is when I try to generate my own timestamp/nonce/signature in my class.
Here's the method from my class that sends the cURL request:
private function execute($options = array())
{
if (!isset($options['url'])) {
return;
}
$ch = curl_init($options['url']);
$method = (isset($options['method'])) ? $options['method'] : 'GET';
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if (isset($options['auth']) && $options['auth']) {
$timestamp = round(microtime(true) * 1000);
$nonce = $this->getNonce(11);
$version = "1.0";
$signatureMethod = "HMAC-SHA1";
$signature = $this->generateSignature($options, $timestamp, $nonce, $signatureMethod, $version);
$authHeader = "Authorization: OAuth oauth_consumer_key=\"{$this->consumerKey}\",oauth_token=\"{$this->accessToken}\",oauth_signature_method=\"{$signatureMethod}\",oauth_timestamp=\"{$timestamp}\",oauth_nonce=\"{$nonce}\",oauth_version=\"{$version}\",oauth_signature=\"{$signature}\"";
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
$authHeader,
"Content-Type: application/json"
));
}
if (isset($options['body']) && !empty($options['body'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($options['body']));
}
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
The getNonce method I copied pretty much directly from https://github.com/BaglerIT/OAuthSimple/blob/master/src/OAuthSimple.php.
Here's the method I've written to generate the signature (which has been cobbled together from various SO posts and other sources):
protected function generateSignature($request, $timestamp, $nonce, $signatureMethod, $version)
{
$base = $request['method'] . "&" . rawurlencode($request['url']) . "&"
. rawurlencode("oauth_consumer_key=" . rawurlencode($this->consumerKey)
. "&oauth_nonce=" . rawurlencode($nonce)
. "&oauth_signature_method=" . rawurlencode($signatureMethod)
. "&oauth_timestamp=" . $timestamp
. "&oauth_version=" . $version);
$key = rawurlencode($this->consumerSecret) . '&' . rawurlencode($this->tokenSecret);
$signature = base64_encode(hash_hmac('sha1', $base, $key, true));
return $signature;
}
I can actually copy and paste the authorization header from Postman into my $authHeader variable, and replace everything except the timestamp/nonce/signature, and it works.
The response I'm getting from their server right now is [code] => 0005 but I can't find anything in their docs about response codes.
Edit: I had missed looking at the response header - the exact error is invalid signature.
There are 2 things I changed to get this to work.
I was missing the oauth_token when creating the base string for the signature
According to the OAuth Core 1.0 documentation, "Parameters are sorted by name, using lexicographical byte value ordering."
So I ended up re-ordering the parameters to be alphabetical. Here's what the code for generating the base string ended up looking like:
$base = $request['method'] . "&" . rawurlencode($request['url']) . "&"
. rawurlencode("oauth_consumer_key=" . rawurlencode($this->consumerKey)
. "&oauth_nonce=" . rawurlencode($nonce)
. "&oauth_signature_method=" . rawurlencode($signatureMethod)
. "&oauth_timestamp=" . rawurlencode($timestamp)
. "&oauth_token=" . rawurlencode($this->accessToken)
. "&oauth_version=" . rawurlencode($version));
I also re-ordered the params in the auth header to match the order of the base string:
$authHeader = "Authorization: OAuth oauth_consumer_key=\"{$this->consumerKey}\",oauth_nonce=\"{$nonce}\",oauth_signature_method=\"{$signatureMethod}\",oauth_timestamp=\"{$timestamp}\",oauth_token=\"{$this->accessToken}\",oauth_version=\"{$version}\",oauth_signature=\"{$signature}\"";
$base = $request['method']
. '&' . rawurlencode($request['url'])
. '&' . rawurlencode('oauth_consumer_key=' . $this->consumerKey)
. rawurlencode('&oauth_nonce=' . $nonce)
. rawurlencode('&oauth_signature_method=' . $signatureMethod)
. rawurlencode('&oauth_timestamp=' . $timestamp)
. rawurlencode('&oauth_version=' . $version)
. rawurlencode('&' . http_build_query($data));
$key = rawurlencode($this->consumerSecret) . '&';
$signature = rawurlencode(base64_encode(hash_hmac('SHA1', $base, $key, true)));
If you do a POST, make sure to include your posted data, otherwise the signature will not validate.
CURLOPT_HTTPHEADER => array(
"authorization: OAuth oauth_consumer_key=\"{$consumerKey}\",oauth_signature_method=\"{$signatureMethod}\",oauth_timestamp=\"{$timestamp}\",oauth_nonce=\"{$nonce}\",oauth_version=\"{$version}\",oauth_signature=\"{$oauthSignature}\"",
"content-type: application/x-www-form-urlencoded",
),
And the header should be as above
This fixed version worked for me :
function generateOauthSignature($method, $url, $consumerKey, $nonce, $signatureMethod, $timestamp, $version, $consumerSecret, $tokenSecret, $tokenValue, $extraParams = array())
{
$base = strtoupper($method) . "&" . rawurlencode($url) . "&"
. rawurlencode("oauth_consumer_key=" . $consumerKey
. "&oauth_nonce=" . $nonce
. "&oauth_signature_method=" . $signatureMethod
. "&oauth_timestamp=" . $timestamp
. "&oauth_token=" . $tokenValue
. "&oauth_version=" . $version);
if (!empty($extraParams)) {
$base .= rawurlencode("&" . http_build_query($extraParams));
}
$key = rawurlencode($consumerSecret) . '&' . rawurlencode($tokenSecret);
$signature = base64_encode(hash_hmac('sha1', $base, $key, true));
return rawurlencode($signature);
}
The following twitter thread helped me : https://twittercommunity.com/t/how-to-generate-oauth-signature-when-post-json-body-in-php/87581
I was also struggling with the proper setup of the OAuth 1 signature and had a lot of failed attempts. After TGA's hint to have a look how it's done with Twitter, I found out that there is an existing class which may be used out-of-the-box:
TwitterAPIExchange.php from the repository https://github.com/J7mbo/twitter-api-php.
Even if is called "Twitter...", it may also be used for other OAuth1 APIs. Calls will look like this:
$settings = array(
'oauth_access_token' => TOKEN,
'oauth_access_token_secret' => TOKEN_SECRET,
'consumer_key' => CONSUMER_KEY,
'consumer_secret' => CONSUMER_SECRET
);
$url = "https://api-url.com/api/v4/users/0451432/";
$requestMethod = 'POST';
$postfields = array(
'groupIds' => '23,24,25',
);
$twitter = new TwitterAPIExchange($settings);
return $twitter->buildOauth($url, $requestMethod)
->setPostfields($postfields)
->performRequest();
It works perfect for me.
This version matches the OAuth PECL library's function so you no longer need it.
public static function oauth_get_sbs(
$requestMethod,
$requestURL,
$request_parameters
): string
{
return $requestMethod . "&" . rawurlencode($requestURL) . "&"
. rawurlencode("oauth_consumer_key=" . rawurlencode($request_parameters['oauth_consumer_key'])
. "&oauth_nonce=" . rawurlencode($request_parameters['oauth_nonce'])
. "&oauth_signature_method=" . rawurlencode($request_parameters['oauth_signature_method'])
. "&oauth_timestamp=" . $request_parameters['oauth_timestamp']
. "&oauth_token=" . $request_parameters['oauth_token']
. "&oauth_version=" . $request_parameters['oauth_version']);
}

update users table through object id without login in parse.com(USING REST API)

I want to update User table of DATA BROWSER using objectId(With out getting user to log in ) using following code.
But I am getting:
error({"code":101,"error":"object not found for update"})
can any one tell me what is wrong with this:
$className = "Classname";
$objectIdToEdit = $_SESSION['objectId'];
$url = 'https://api.parse.com/1/classes/' . $className . '/' . $objectIdToEdit;
$appId = '***********************';
$restKey = '***********';
$updatedData = '{"firstname":"Billie123"}';
$rest = curl_init();
curl_setopt($rest,CURLOPT_URL,$url);
curl_setopt($rest,CURLOPT_PORT,443);
curl_setopt($rest,CURLOPT_CUSTOMREQUEST,"PUT");
curl_setopt($rest,CURLOPT_RETURNTRANSFER, true);
curl_setopt($rest,CURLOPT_POSTFIELDS,$updatedData);
curl_setopt($rest,CURLOPT_HTTPHEADER, array(
"X-Parse-Application-Id: " . $appId,
"X-Parse-Master-Key: " . $restKey,
"Content-Type: application/json")
);
$response = curl_exec($rest);
echo $response;
I solved problem my self ,URL I was using is to save data
$url = 'https://api.parse.com/1/classes/' . $className . '/' . $objectIdToEdit;
I just changed URL to update data and problem is solved
$url = 'https://api.parse.com/1/' . $className . '/' . $objectIdToEdit;
thanks Ghost for editing

Google API Get inbox emails using access_token?

I am able to get access_token for multiple permissions like emails, contacts, docs, etc. using oAuth 2.0. I have access_token
I got contacts using the following code.
$url = 'https://www.google.com/m8/feeds/contacts/default/full?max- results='.$max_results.'&oauth_token='.$access_token;
$response_contacts= curl_get_file_contents($url);
Now i want to get users Emails using this access_token.
i used this url . but it gives 401 unauthorized Error
$url = 'https://mail.google.com/mail/feed/atom&oauth_token='.$access_token;
$response_emails= curl_get_file_contents($url);
please guide me how can i get emails using access_token.
I've seen references to the Gmail feed using oauth_token as a request parameter. However, once I used the OAuth Playground I discovered that you need to pass your OAuth information as an Authorization header, as you'll see below.
<?php
$now = time();
$consumer = ...; // your own value here
$secret = ...; // your own value here
$nonce = ...; // same value you've been using
$algo = "sha1";
$sigmeth = "HMAC-SHA1";
$av = "1.0";
$scope = "https://mail.google.com/mail/feed/atom";
$path = $scope;
$auth = ...; // an object containing outputs of OAuthGetAccessToken
$args = "oauth_consumer_key=" . urlencode($consumer) .
"&oauth_nonce=" . urlencode($nonce) .
"&oauth_signature_method=" . urlencode($sigmeth) .
"&oauth_timestamp=" . urlencode($now) .
"&oauth_token=" . urlencode($auth->oauth_token) .
"&oauth_version=" . urlencode($av);
$base = "GET&" . urlencode($path) . "&" . urlencode($args);
$sig = base64_encode(hash_hmac($algo, $base,
"{$secret}&{$auth->oauth_token_secret}", true));
$url = $path . "?oauth_signature=" . urlencode($sig) . "&" . $args;
// Create a stream
$opts = array(
"http" => array(
"method" => "GET",
"header" => "Authorization: OAuth " .
"oauth_version=\"{$av}\", " .
"oauth_nonce=\"{$nonce}\", " .
"oauth_timestamp=\"{$now}\", " .
"oauth_consumer_key=\"{$consumer}\", " .
"oauth_token=\"{$auth->oauth_token}\", " .
"oauth_signature_method=\"{$sigmeth}\", " .
"oauth_signature=\"{$sig}\"\r\n"
)
);
$context = stream_context_create($opts);
$out = file_get_contents($path, false, $context);
?>

Getting wrong signature in Flickr API on PHP

I am facing some problem in generating oauth_signature for flickr api. Can you please look into this and advise me what I am doing wrong?
// p.s. I am sharing my Flickr key and secret as I will change them when I will start production development.
Code
/* PHP code */
$NONCE=base64_decode(rand());
$TIMESTAMP= gmdate('U');
$SECRET="39b4f5fd592ede81";
$KEY="1bab082052d7cf8b3aa9e2bc92882ac0";
$CONSUMER_SECRET= $SECRET. "&";
$url_1 = "http://www.flickr.com/services/oauth/request_token?";
$url_1 = urlencode($url_1);
$url_2 = "oauth_callback=http%3A%2F%2Flocalhost%2FFlickr%2Flogin.php&oauth_consumer_key=". $KEY;
$url_2 .="&oauth_nonce=". $NONCE. "&oauth_signature_method=HMAC-SHA1&oauth_timestamp=". $TIMESTAMP. "&oauth_version=1.0";
// generate signature
$BASE_STRING ="";
$BASE_STRING .= "GET&". urlencode($url_1). urlencode($url_2);
$API_SIG= base64_encode(hash_hmac("sha1",$BASE_STRING,$CONSUMER_SECRET, true) );
// url generate
$url="http://www.flickr.com/services/oauth/request_token?oauth_callback=http://localhost/Flickr/login.php&oauth_consumer_key=". $KEY;
$url.="&oauth_nonce=". $NONCE. "&oauth_timestamp=". $TIMESTAMP. "&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_signature=". $API_SIG;
// calling
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_REFERER, "http://www.example.org/yay.htm");
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla Firefox/3.0");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data= curl_exec($ch);
echo $data;
curl_close($ch);
This worked for me, hopefully it helps someone else...
<?php
$consumerKey = 'your_Flickr_key';
$consumerSecret = 'your_Flickr_secret';
$requestTokenUrl = "https://www.flickr.com/services/oauth/request_token";
$oauthTimestamp = time();
$nonce = md5(mt_rand());
$oauthSignatureMethod = "HMAC-SHA1";
$oauthVersion = "1.0";
$sigBase = "GET&" . rawurlencode($requestTokenUrl) . "&"
. rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
. "&oauth_nonce=" . rawurlencode($nonce)
. "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
. "&oauth_timestamp=" . $oauthTimestamp
. "&oauth_version=" . $oauthVersion);
$sigKey = $consumerSecret . "&";
$oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));
$requestUrl = $requestTokenUrl . "?"
. "oauth_consumer_key=" . rawurlencode($consumerKey)
. "&oauth_nonce=" . rawurlencode($nonce)
. "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
. "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
. "&oauth_version=" . rawurlencode($oauthVersion)
. "&oauth_signature=" . rawurlencode($oauthSig);
$response = file_get_contents($requestUrl);
var_export($response);
I am answering my own question. Thanks to Sam Judson: http://www.wackylabs.net
I removed base64_decode() from generating random numbers and it just did worked.

Categories