AWS V4 Signature

Concepts Involved

  • Canonical Request
  • String to sign
  • Signing key
  • Signature

Flow:

1. Canonical-Request    => Create StringToSign Using Canonical Request
2. Create SigningKey Using Date, SecretKey, Region  =>   (Independent Step)
3. Create Signature = HMAC-SHA25(SigningKey, StringToSign)

The entire algorithm should be reconstructable at server side to verify signature.

Example Request and Response

Request :

> GET /dev/product?id=1 HTTP/1.1

> Host: egup58p5n5.execute-api.us-east-1.amazonaws.com

> X-Amz-Date: 20190430T121358Z

> Authorization: AWS4-HMAC-SHA256 
                 Credential=AKIAIGODE5VFHYMUCXQQ/20190430/
                            us-east-1/execute-api/aws4_request, 
                 SignedHeaders=host;x-amz-date, 
                 Signature=2c57f9f93b4f3abb8376....xxxx

Note:

  • Authorization header provides Algo followed by key-value pairs.
  • Request provides a timestamp.
  • Authorization clearly says which headers should be considered for signing. host and date is mandatory, you can add more headers if you like.
  • Request provides signature in Authorization header.

Response:

Content-Type: application/json
x-amzn-RequestId: 6f465fec-6b41-11e9-ae05-0fe339eafd1c
x-amz-apigw-id: Y85RLGMnIAMF5dg=
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *

X-Amzn-Trace-Id: Root=1-5cc83c07-4c26c45edd16ccd50a833482;Sampled=0
X-Cache: Miss from cloudfront
Via: 1.1 f06e07dccfc8e669cf9b4cfe75b40700.cloudfront.net (CloudFront)
X-Amz-Cf-Id: i8poETGlc9p90ov3aNAoRMsBIBlCWxzZZQgoqcxJ5AFC4ykQ5g16Zw==

Note:

  • Response includes Access-Control-Allow-Methods/Origin : * (Support CORS)
  • Requester does not know anything about x-amz-RequestId and x-amz-apigw-id. Useful for Tracing ???

Canonical Request

It is something that we construct from given request. It is also something that we construct from given server. So ask "how can I construct at client and server side ?" for each element.

Example Canonical Request:

GET               # HTTP Verb. Newline at the end.

egup58p5n5.execute-api.us-east-1.amazonaws.com  # Canonical URI. UriEncoded
                                  # API endpoint host.

p1=v1&p2=v2       # Canonical Query String. UriEncoded. 
                  # Sorted Alphabatically based on Query Parameters

host:myhost       # Canonical Headers. All lowercased and trimmed.
header2:value2    # How does server know which headers to include here ?
                  # Answer: Only signed-headers are included here.

host; content-type;  # Signed Headers. Lowercased. ; separated.

...hash-payload-hex..  # Hashed Payload. Hex(SHA256Hash(<payload>)
                     # Payload is a string.

StringToSign

AWS4-HMAC-SHA256        # Sign Algo. There is Default. Specified in Request.
20190430T000000Z        # TimeStamp. As in X-Amz-date in request header.

                        # Scope: Date/Region/Service/aws4_request
20190430/us-east-2/execute-api/aws4_request

Hex(SHA256Hash(CanonicalRequest))

So String to Sign includes entire headers, payload, date, region and service. If any of these is tampered with, we will know.

Signing Key

::
# Define SHA := HMAC-SHA256 DateKey = SHA( "AWS4" + <SecretKey> + Date_YYYYMMDD ) DateRegionKey = SHA( DateKey, <AWS-Region> ) DateServiceKey = SHA( DateRegionKey, <AWS-Region> ) SigningKey = SHA( DateServiceKey, 'aws4_request')

So signing key is fixed for a service and region except for "Date" (not timestamp).

Signature

Signature = Hex(HMAC-SHA256(SigningKey, StringToSign))

The signature is included in request as special header :

Example authentication parameters in a query string:

Action=ListUsers&
Version=2010-05-08&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIDEXAMPLE%2F20150830%2Fus-east-1%2Fiam%2Faws4_request&
X-Amz-Date=20150830T123600Z&
X-Amz-SignedHeaders=content-type%3Bhost%3Bx-amz-date