AWS V4 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.
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:
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:
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.
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.
::
# 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 = 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