Amazon Web Services General Reference
  (Version 1.0)
« PreviousNext »
Go to the AWS Discussion Forum for this product.Did this page help you?  Yes | No |  Tell us about it...

Task 1: Create a Canonical Request For Signature Version 4

You must standardize the format of your request to the same format AWS uses when it recalculates your signature. Formatting the request into an unambiguous, canonical format before signing it, ensures that you and AWS will calculate the same signature that you calculated for a given request. Non-standardized formatting can result in different HMAC signatures.

You create a canonical request by concatenating the following components:

Canonical request pseudocode

CanonicalRequest =
  HTTPRequestMethod + '\n' +
  CanonicalURI + '\n' +
  CanonicalQueryString + '\n' +
  CanonicalHeaders + '\n' +
  SignedHeaders + '\n' +
  HexEncode(Hash(Payload))

In the previous example, the HexEncode function represents a function that returns the base-16 encoding of its sole input value using lowercase characters. For example, HexEncode("m") returns the value 6d rather than 6D. Each input byte must be represented as exactly two hexadecimal characters.

To show you how to construct the canonical form of an HTTP or HTTPS request, let's use the following sample request:

Sample request

POST http://iam.amazonaws.com/ HTTP/1.1
host: iam.amazonaws.com
Content-type: application/x-www-form-urlencoded; charset=utf-8
x-amz-date: 20110909T233600Z

Action=ListUsers&Version=2010-05-08

The sample request is a POST request (method) that makes a ListUsers API (action) call to AWS Identity and Access Management (host).

To create a canonical request, concatenate the following components from each step into a single string:

  1. Start with the HTTP request method (GET, PUT, POST, etc.).

    Sample request: HTTPRequestMethod

    POST

  2. Add the CanonicalURI parameter. This is the URI-encoded version of the absolute path component of the URI—everything from the HTTP host header to the question mark character ('?') that begins the query string parameters. Then add a newline character.

    Normalize URI paths according to RFC 3986 by removing redundant and relative path components. Each path segment must be URI-encoded. If the absolute path is empty, use a forward slash (/).

    Sample request: CanonicalURI

    /

  3. Add the CanonicalQueryString parameter. If the request does not include a query string, set the value of CanonicalQueryString to an empty string.

    Sample request: CanonicalQueryString

    empty string

    To construct the canonical query string, complete the following steps:

    1. URI encode each parameter name and value according to the following rules:

      • Do not URL-encode any of the unreserved characters that RFC 3986 defines: A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), and tilde ( ~ ).

      • Percent-encode all other characters with %XY, where X and Y are hexadecimal characters (0-9 and uppercase A-F).

        For example, the space character must be encoded as %20 (and not '+', as some encoding schemes do) and extended UTF-8 characters must be in the form %XY%ZA%BC.

    2. Sort the encoded parameter names by character code (that is, in strict ASCII order). For example, a parameter name that begins with the uppercase letter F (ASCII code 70) precedes a parameter name that begins with a lowercase letter b (ASCII code 98).

    3. Build the canonical query string by starting with the first parameter name in the sorted list.

    4. For each parameter, append the URI-encoded parameter name, followed by the character '=' (ASCII code 61), followed by the URI-encoded parameter value.

      Use an empty string for parameters that have no value.

    5. Append the character '&' (ASCII code 38) after each parameter value except for the last value in the list.

    In some cases, you might construct a request where all the authorization parameters are in the query string, such as pre-signed URLs for Amazon S3. In such cases, the canonical query string must include the algorithm, credential scope, date, and signed headers parameters, as shown in the following example:

    Authorization parameters in a query string

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

    The previous example was formatted for human readability. The canonical query string must be one continuous line of text. For more information about the authorization parameters, see Task 2: Create a String to Sign for Signature Version 4.

  4. Add the CanonicalHeaders parameter, which is a list of all the HTTP headers for the request. You must include a valid host header. Any other required headers are described by the service you're using.

    Sample request: CanonicalHeaders

    content-type:application/x-www-form-urlencoded; charset=utf-8\n
    host:iam.amazonaws.com\n
    x-amz-date:20110909T233600Z\n

    To create a canonical headers list, convert all header names to lowercase and trim excess white space characters out of the header values. When you trim, remove leading spaces and trailing spaces, and convert sequential spaces in the value to a single space. However, do not remove extra spaces from any values that are inside quotation marks.

    The following pseudocode describes how to construct the canonical list of headers:

    CanonicalHeaders =
    CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ... + CanonicalHeadersEntryN
    CanonicalHeadersEntry =
    LOWERCASE(HeaderName) + ':' + TRIMALL(HeaderValue) + '\n'

    LOWERCASE represents a function that converts all characters to lowercase. TRIMALL represents a function that removes excess white space before and after values and from inside non-quoted strings, per RFC 2616 Section 4.2.

    Build the canonical headers list by iterating through the collection of header names, sorted by lowercase character code. Construct each header by using the following rules:

    • Append the lowercase header name followed by a colon.

    • Append a comma-separated list of values for that header. If there are duplicate headers, the values are comma-separated. Do not sort the values in headers that have multiple values.

    • Append a newline ('\n').

    To show an additional example, the following two samples compare a more complex set of headers with their canonical form:

    Original headers

    host:iam.amazonaws.com\n
    Content-type:application/x-www-form-urlencoded; charset=utf-8\n
    My-header1:    a   b   c \n
    x-amz-date:20120228T030031Z\n
    My-Header2:    "a   b   c"\n
    

    Canonical form

    content-type:application/x-www-form-urlencoded; charset=utf-8\n
    host:iam.amazonaws.com\n
    my-header1:a b c\n
    my-header2:"a   b   c"\n
    x-amz-date:20120228T024136Z\n

    In the canonical form, the following changes were done:

    • The header names have been converted to lowercase characters.

    • The headers have been sorted by character code.

    • Removed extra spaces for the value of my-header1 because it's not in quotation marks.

    • No extra spaces were removed for the value of my-header2 because it's in quotation marks.

    • Removed leading spaces for the my-header1 and my-header2 values.

  5. Add the SignedHeaders parameter, which is the list of HTTP headers that you included in the canonical headers. You must include a list of signed headers because extra headers are often added to the request by the transport layers. The list of signed headers enables AWS to determine which headers are part of your original request.

    The host header must be included as a signed header. If you include a date or x-amz-date header, you must also include that header in the list of signed headers.

    Sample request: SignedHeaders

    content-type;host;x-amz-date

    To create the signed headers list, convert all header names to lowercase, sort them by character code, and use a semicolon to separate the header names. The following pseudocode describes how to construct a list of signed headers. LOWERCASE represents a function that converts all characters to lowercase.

    SignedHeaders =
    LOWERCASE(HeaderName0) + ';' + LOWERCASE(HeaderName1) + ... + LOWERCASE(HeaderNameN)

    Build the signed headers list by iterating through the collection of header names, sorted by lowercase character code.

    For each header name except the last, append a semicolon (';') to the header name to separate it from the following header name.

  6. Add the payload, which you derive from the body of the HTTP or HTTPS request.

    To create the payload, use a signing algorithm to encode the body of the HTTP or HTTPS request. If the payload is empty, use the empty string as the input to the hash function. The resulting payload is the lowercase hex (base-16) encoded digest.

    The following example uses SHA256 to hash the payload:

    Sample request: Payload

    Action=ListUsers&Version=2010-05-08

    Sample request: Hashed payload

    b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2

    Note

    When you create the string to sign (Task 2: Create a String to Sign for Signature Version 4), you will specify the signing algorithm that you used to hash the payload. For example, if you used SHA256, you will specify AWS4-HMAC-SHA256 as the signing algorithm.

  7. To construct the final canonical request, combine all the components from each step as a single string. If you follow the canonical request pseudocode, the resulting canonical request is shown in the following example:

    Sample request: Canonical form

    POST
    /
    
    content-type:application/x-www-form-urlencoded; charset=utf-8
    host:iam.amazonaws.com
    x-amz-date:20110909T233600Z
    
    content-type;host;x-amz-date
    b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2

  8. Hash the canonical request by using the same algorithm from that you used to hash the payload.

    The hashed canonical request is lowercase base-16 encoded. The following sample example uses SHA256 to hash the canonical request:

    Sample request: Hashed canonical request

    3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2

    You include the hashed canonical request as part of the string to sign in Task 2: Create a String to Sign for Signature Version 4.