9

I am trying to create Authorized Signature to access IAM secured API gateway endpoint.

$alg = "SHA256";
$CanonicalRequest = "GET\n/dev/pets\n\nhost:3r4fgts8e5.execute-api.ap-northeast-1.amazonaws.com\nx-amz-date:".$dd."\n\nhost;x-amz-date\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

$CR =  str_replace("\n", "", $CanonicalRequest);
$CR = str_replace("\r", "", $CR);
$CR = strtolower($CR);

$StringToSign  = "AWS4-HMAC-SHA256\n".$dd."\n".$date->format( 'Ymd' )."/ap-northeast-1/execute-api/aws4_request\n".hash( $alg, $CR )."";
            
// 1) HMACs
$kSecret = 'AWS4' . $secret_key;
$kDate = hash_hmac( $alg, $date->format( 'Ymd' ), $kSecret, true );     
$kRegion = hash_hmac( $alg, $region, $kDate, true );
$kService = hash_hmac( $alg, $service, $kRegion, true );
$kSigning = hash_hmac( $alg, 'aws4_request', $kService, true );     
$signature = hash_hmac( $alg, $StringToSign, $kSigning );       

$authorization = array(
    'Credential=' . $access_key . '/' . implode( '/', $scope ),
    'SignedHeaders=' . implode( ';', array_keys( $can_headers ) ),
    'Signature=' . $signature,
);
$authorization = $request['algorithm'] . ' ' . implode( ',', $authorization );
$request['Authorization'] = $authorization;

But I am getting "The request signature we calculated does not match the signature you provided" error

"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.\n\nThe Canonical String for this request should have been\n'GET\n/dev/pets\n\nhost:3r4fgts8e5.execute-api.ap-northeast-1.amazonaws.com\nx-amz-date:20161002T231640Z\n\nhost;x-amz-date\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20161002T231640Z\n20161002/ap-northeast-1/execute-api/aws4_request\n0b8c12e0a5f21137c5739a9d26056dfb081218631a9adcf37db1d2e09a014c4e'\n"

My String-to-sign string is

"AWS4-HMAC-SHA256
20161002T231640Z
20161002/ap-northeast-1/execute-api/aws4_request
fb4f7ebdcb405bceed598ecc097267b929eeb3f8f075b1b7a776f53c8c8c6168"

which is totally different from what AWS expected in signature.

Machavity
  • 30,841
  • 27
  • 92
  • 100
Pravesh Khatana
  • 1,034
  • 1
  • 11
  • 18
  • *"which is totally different from what AWS expected in signature."* So, you have your answer -- your code is not building the correct canonical string, so it can't possibly build the correct string to sign, nor can it generate the correct signature. Your next course of action seems clear -- review the documentation and correct your code to generate the correct intermediate values. Note the words **"should have been"** occurring twice in the error message. AWS can't tell what values you actually used -- signing is not reversible -- they can only calculate what you *should have used*. – Michael - sqlbot Oct 03 '16 at 02:19

1 Answers1

8

Here is the solution

private function signRequest(){
        $method ='GET';
        $uri = '/dev';
        $json = file_get_contents('php://input');
        $obj = json_decode($json);


        if(isset($obj->method))
        {
            $m = explode("|", $obj->method);
            $method = $m[0];
            $uri .= $m[1];
        }


        $secretKey = $this->session->data['aws_secret'];
        $access_key = $this->session->data['aws_key'];
        $token = $this->session->data['aws_token'];
        $region = 'ap-southeast-1';
        $service = 'execute-api';

        $options = array(); $headers = array();
        $host = "YOUR-API-HOST.execute-api.ap-southeast-1.amazonaws.com";
//Or you can define your host here.. I am using API gateway.


        $alg = 'sha256';

        $date = new DateTime( 'UTC' );

        $dd = $date->format( 'Ymd\THis\Z' );

        $amzdate2 = new DateTime( 'UTC' );
        $amzdate2 = $amzdate2->format( 'Ymd' );
        $amzdate = $dd;

        $algorithm = 'AWS4-HMAC-SHA256';


        $parameters = (array) $obj->data;

           if($obj->data == null || empty($obj->data)) 
        {
            $obj->data = "";
        }else{
            $param = json_encode($obj->data);
            if($param == "{}")
            {
                $param = "";

            }

        $requestPayload = strtolower($param);
        $hashedPayload = hash($alg, $requestPayload);

        $canonical_uri = $uri;
        $canonical_querystring = '';

        $canonical_headers = "content-type:"."application/json"."\n"."host:".$host."\n"."x-amz-date:".$amzdate."\n"."x-amz-security-token:".$token."\n";
        $signed_headers = 'content-type;host;x-amz-date;x-amz-security-token';
        $canonical_request = "".$method."\n".$canonical_uri."\n".$canonical_querystring."\n".$canonical_headers."\n".$signed_headers."\n".$hashedPayload;


        $credential_scope = $amzdate2 . '/' . $region . '/' . $service . '/' . 'aws4_request';
        $string_to_sign  = "".$algorithm."\n".$amzdate ."\n".$credential_scope."\n".hash('sha256', $canonical_request)."";
       //string_to_sign is the answer..hash('sha256', $canonical_request)//

        $kSecret = 'AWS4' . $secretKey;
        $kDate = hash_hmac( $alg, $amzdate2, $kSecret, true );
        $kRegion = hash_hmac( $alg, $region, $kDate, true );
        $kService = hash_hmac( $alg, $service, $kRegion, true );
        $kSigning = hash_hmac( $alg, 'aws4_request', $kService, true );     
        $signature = hash_hmac( $alg, $string_to_sign, $kSigning ); 
        $authorization_header = $algorithm . ' ' . 'Credential=' . $access_key . '/' . $credential_scope . ', ' .  'SignedHeaders=' . $signed_headers . ', ' . 'Signature=' . $signature;

        $headers = [
                    'content-type'=>'application/json', 
                    'x-amz-security-token'=>$token, 
                    'x-amz-date'=>$amzdate, 
                    'Authorization'=>$authorization_header];
        return $headers;

    }
Pravesh Khatana
  • 1,034
  • 1
  • 11
  • 18
  • How I can parse this signature that is generated on login request and now I want to validate and parse this signature to validate ? – Rizwan Saleem Jul 13 '18 at 11:42
  • are you using aws API? If yes, then you have to pass this signed header to api – Pravesh Khatana Jul 13 '18 at 20:18
  • Any way to provide this in a procedural manner? OOP goes straight over my head. – Erik Thiart Oct 12 '18 at 10:26
  • 3
    I have implemented in procedural manner - https://github.com/avi-wish/aws4-signature-php – Avik Das Mar 01 '19 at 14:41
  • 1
    I have similar issue. Things are working with postman but when try to use editor it doesn't return session token but error 'SignatureDoesNotMatch'. I used function as mentioned here at: stackoverflow.com/a/42816847/6760546 Any idea what can be wrong with code? PS: I'm trying to use service 'GetSessionToken' – Nilesh Apr 13 '23 at 17:54