Standej Standej - 1 year ago 69
PHP Question

AWS API GET Request getting response "SignatureDoesNotMatch"

Im writing a AWS API request to list users on IAM AWS service. And I'm receiving error message.

<ErrorResponse xmlns="">
<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


The String-to-Sign should have been

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


and when I use functions
$hashedcanon = hash_hmac("sha256", $canonicalrequest, True);
in my code im getting


Here is my code.

$AWSAccessKeyId = "<myaccesskey>";
$SecretAccessKey = "<mysecretkey>";
$timestamp = date('Ymd',time()).'T'.date('His',time()).'Z';
$date = date('Ymd',time());
$url = '';
$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".

$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
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.

Answer Source
$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.