Standej Standej - 5 months ago 34
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="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&amp;Version=2010-05-08&amp;X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIAIMPILWMPQSH57DNA%2F20160621%2Fus-east-1%2Fiam%2Faws4_request&amp;X-Amz-Date=20160621T142939Z&amp;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

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