Signature Version 4 Signing Process in PHP to access API Gateway endpoint

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.

Answers 1

  • 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;
    
        }
    

Related Articles