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.

1
  • "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. Commented Oct 3, 2016 at 2:19

1 Answer 1

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;

    }
Sign up to request clarification or add additional context in comments.

5 Comments

How I can parse this signature that is generated on login request and now I want to validate and parse this signature to validate ?
are you using aws API? If yes, then you have to pass this signed header to api
Any way to provide this in a procedural manner? OOP goes straight over my head.
I have implemented in procedural manner - github.com/avi-wish/aws4-signature-php
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'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.