Create a signed AWS API request - AWS Identity and Access Management

Create a signed AWS API request

Important

If you use an AWS SDKs (see Sample Code and Libraries) or AWS command line (CLI) tool to send API requests to AWS, you can skip this section because the SDK and CLI clients authenticate your requests by using the access keys that you provide. Unless you have a good reason not to, we recommend that you always use an SDK or the CLI.

In Regions that support multiple signature versions, manually signing requests mean you must specify which signature version is used. When you supply requests to Multi-Region Access Points, SDKs and the CLI automatically switch to using Signature Version 4A without additional configuration.

The following is an overview of the process to create a signed request. To calculate a signature, you first need a string to sign. You then calculate a HMAC-SHA256 hash of the string to sign by using a signing key. The following diagram illustrates the process, including the various components of the string that you create for signing.


                An image of the parts of a canonical request, string to sign, signing key,
                    and signature.

The following table describes the functions that are shown in the diagram. You need to implement code for these functions. For more information, see the code examples in the AWS SDKs.

Function Description

Lowercase()

Convert the string to lowercase.

Hex()

Lowercase base 16 encoding.

SHA256Hash()

Secure Hash Algorithm (SHA) cryptographic hash function.

HMAC-SHA256()

Computes HMAC by using the SHA256 algorithm with the signing key provided. This is the final signature.

Trim()

Remove any leading or trailing whitespace.

UriEncode()

URI encode every byte. UriEncode() must enforce the following rules:

  • URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'.

  • The space character is a reserved character and must be encoded as "%20" (and not as "+").

  • Each URI encoded byte is formed by a '%' and the two-digit hexadecimal value of the byte.

  • Letters in the hexadecimal value must be uppercase, for example "%1A".

  • Encode the forward slash character, '/', everywhere except in the object key name. For example, if the object key name is photos/Jan/sample.jpg, the forward slash in the key name is not encoded.

Important

The standard UriEncode functions provided by your development platform may not work because of differences in implementation and related ambiguity in the underlying RFCs. We recommend that you write your own custom UriEncode function to ensure that your encoding will work.

To see an example of a UriEncode function in Java, see Java Utilities on the GitHub website.

Note

When signing your requests, you can use either AWS Signature Version 4 or AWS Signature Version 4A. The key difference between the two is determined by how the signature is calculated. With AWS Signature Version 4A, the signature does not include Region-specific information and is calculated using the AWS4-ECDSA-P256-SHA256 algorithm.

Temporary security credentials

Instead of using long-term credentials to sign a request, you can use temporary security credentials provided by AWS Security Token Service (AWS STS).

When you use temporary security credentials, you must add X-Amz-Security-Token to the Authorization header or the query string to hold the session token. Some services require that you add X-Amz-Security-Token to the canonical request. Other services require only that you add X-Amz-Security-Token at the end, after you calculate the signature. Check the documentation for each AWS service for details.

Summary of signing steps

Step 1: Create a canonical request

Arrange the contents of your request (host, action, headers, etc.) into a standard canonical format. The canonical request is one of the inputs used to create a string to sign. For details, see Elements of an AWS API request signature.

Step 2: Create a hash of the canonical request

Derive a signing key by performing a succession of keyed hash operations (HMAC operations) on the request date, Region, and service, with your AWS secret access key as the key for the initial hashing operation.

Step 3: Create a String to Sign

Create a string to sign with the canonical request and extra information such as the algorithm, request date, credential scope, and the digest (hash) of the canonical request.

Step 4: Calculate the signature

After you derive the signing key, you then calculate the signature by performing a keyed hash operation on the string to sign. Use the derived signing key as the hash key for this operation.

Step 5: Add the signature to the request

After you calculate the signature, add it to an HTTP header or to the query string of the request.

Step 1: Create a canonical request

Create a canonical request by concatenating the following strings, separated by newline characters. This helps ensure that the signature that you calculate and the signature that AWS calculates can match.

<HTTPMethod>\n <CanonicalURI>\n <CanonicalQueryString>\n <CanonicalHeaders>\n <SignedHeaders>\n <HashedPayload>
  • HTTPMethod – The HTTP method, such as GET, PUT, HEAD, and DELETE.

  • CanonicalUri – The URI-encoded version of the absolute path component URI, starting with the "/" that follows the domain name and up to the end of the string or to the question mark character ('?') if you have query string parameters. If the absolute path is empty, use a forward slash character (/). The URI in the following example, /examplebucket/myphoto.jpg, is the absolute path and you don't encode the "/" in the absolute path:

    http://s3.amazonaws.com/examplebucket/myphoto.jpg
  • CanonicalQueryString – The URI-encoded query string parameters. You URI-encode each name and values individually. You must also sort the parameters in the canonical query string alphabetically by key name. The sorting occurs after encoding. The query string in the following URI example is:

    http://s3.amazonaws.com/examplebucket?prefix=somePrefix&marker=someMarker&max-keys=2

    The canonical query string is as follows (line breaks are added to this example for readability):

    UriEncode("marker")+"="+UriEncode("someMarker")+"&"+ UriEncode("max-keys")+"="+UriEncode("20") + "&" + UriEncode("prefix")+"="+UriEncode("somePrefix")

    When a request targets a subresource, the corresponding query parameter value will be an empty string (""). For example, the following URI identifies the ACL subresource on the examplebucket bucket:

    http://s3.amazonaws.com/examplebucket?acl

    The CanonicalQueryString in this case is as follows:

    UriEncode("acl") + "=" + ""

    If the URI does not include a '?', there is no query string in the request, and you set the canonical query string to an empty string (""). You will still need to include the "\n".

  • CanonicalHeaders – A list of request headers with their values. Individual header name and value pairs are separated by the newline character ("\n"). The following is an example of a canonicalheader:

    Lowercase(<HeaderName1>)+":"+Trim(<value>)+"\n" Lowercase(<HeaderName2>)+":"+Trim(<value>)+"\n" ... Lowercase(<HeaderNameN>)+":"+Trim(<value>)+"\n"

    CanonicalHeaders list must include the following:

    • HTTP host header.

    • If the Content-Type header is present in the request, you must add it to the CanonicalHeaders list.

    • Any x-amz-* headers that you plan to include in your request must also be added. For example, if you are using temporary security credentials, you need to include x-amz-security-token in your request. You must add this header in the list of CanonicalHeaders.

    Note

    The x-amz-content-sha256 header is required for Amazon S3 AWS requests. It provides a hash of the request payload. If there is no payload, you must provide the hash of an empty string.

    Each header name must:

    • use lowercase characters.

    • appear in alphabetical order.

    • be followed by a colon (:).

    For values, you must:

    • trim any leading or trailing spaces.

    • convert sequential spaces to a single space.

    • separate the values for a multi-value header using commas.

    • You must include the host header (HTTP/1.1) or the :authority header (HTTP/2), and any x-amz-* headers in the signature. You can optionally include other standard headers in the signature, such as content-type.

    The Lowercase() and Trim() functions used in this example are described in the preceding section.

    The following is an example CanonicalHeaders string. The header names are in lowercase and sorted.

    host:s3.amazonaws.com x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-amz-date:20130708T220855Z

    Note

    For the purpose of calculating an authorization signature, only the host and any x-amz-* headers are required; however, in order to prevent data tampering, you should consider including all the headers in the signature calculation.

  • SignedHeaders – An alphabetically sorted, semicolon-separated list of lowercase request header names. The request headers in the list are the same headers that you included in the CanonicalHeaders string. For example, for the previous example, the value of SignedHeaders would be as follows:

    host;x-amz-content-sha256;x-amz-date
  • HashedPayload – A string created using the payload in the body of the HTTP request as input to a hash function. This string uses lowercase hexadecimal characters.

    Hex(SHA256Hash(<payload>)

    If there is no payload in the request, you compute a hash of the empty string as follows:

    Hex(SHA256Hash(""))

    The hash returns the following value:

    e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

    For example, when you upload an object by using a PUT request, you provide object data in the body. When you retrieve an object by using a GET request, you compute the empty string hash.

Step 2: Create a hash of the canonical request

Create a hash (digest) of the canonical request using the same algorithm that you used to create the hash of the payload. The hash of the canonical request is a string of lowercase hexadecimal characters.

Step 3: Create a string to sign

Create a string by concatenating the following strings, separated by newline characters. Do not end this string with a newline character.

Algorithm \n RequestDateTime \n CredentialScope \n HashedCanonicalRequest
  • Algorithm – The algorithm used to create the hash of the canonical request. For SHA-256, the algorithm is AWS4-HMAC-SHA256.

  • RequestDateTime – The date and time used in the credential scope. This value is the current UTC time in ISO 8601 format (for example, 20130524T000000Z).

  • CredentialScope – The credential scope. This restricts the resulting signature to the specified Region and service. The string has the following format: YYYYMMDD/region/service/aws4_request.

  • HashedCanonicalRequest – The hash of the canonical request. This value is calculated in Step 2.

The following is an example string to sign.

"AWS4-HMAC-SHA256" + "\n" + timeStampISO8601Format + "\n" + <Scope> + "\n" + Hex(SHA256Hash(<CanonicalRequest>))

Step 4: Calculate the signature

In AWS Signature Version 4, instead of using your AWS access keys to sign a request, you create a signing key that is scoped to a specific Region and service as the authentication information you'll add to your request.

DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>") DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>") DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>") SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")

For a list of Region strings, see Regional Endpoints in the AWS General Reference.

For each step, call the hash function with the required key and data. The result of each call to the hash function becomes the input for the next call to the hash function.

Required input
  • A string, Key, that contains your secret access key

  • A string, Date, that contains the date used in the credential scope, in the format YYYYMMDD

  • A string, Region, that contains the Region code (for example, us-east-1)

  • A string, Service, that contains the service code (for example, ec2)

  • The string to sign that you created in the previous step.

To calculate the signature
  1. Concatenate "AWS4" and the secret access key. Call the hash function with the concatenated string as the key and the date string as the data.

    kDate = hash("AWS4" + Key, Date)
  2. Call the hash function with the result of the previous call as the key and the Region string as the data.

    kRegion = hash(kDate, Region)
  3. Call the hash function with the result of the previous call as the key and the service string as the data.

    kService = hash(kRegion, Service)
  4. Call the hash function with the result of the previous call as the key and "aws4_request" as the data.

    kSigning = hash(kService, "aws4_request")
  5. Call the hash function with the result of the previous call as the key and the string to sign as the data. The result is the signature as a binary value.

    signature = hash(kSigning, string-to-sign)
  6. Convert the signature from binary to hexadecimal representation, in lowercase characters.

Step 5: Add the signature to the request

Example: Authorization header

The following example shows an Authorization header for the DescribeInstances action. For readability, this example is formatted with line breaks. In your code, this must be a continuous string. There is no comma between the algorithm and Credential. However, the other elements must be separated by commas.

Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=calculated-signature
Example: Request with authentication parameters in the query string

The following example shows a query for the DescribeInstances action that includes the authentication information. For readability, this example is formatted with line breaks and is not URL encoded. In your code, the query string must be a continuous string that is URL encoded.

https://ec2.amazonaws.com/? Action=DescribeInstances& Version=2016-11-15& X-Amz-Algorithm=AWS4-HMAC-SHA256& X-Amz-Credential=AKIAIOSFODNN7EXAMPLE/20220830/us-east-1/ec2/aws4_request& X-Amz-Date=20220830T123600Z& X-Amz-SignedHeaders=host;x-amz-date& X-Amz-Signature=calculated-signature

Source code in the AWS SDKs

The AWS SDKs include source code on GitHub for signing AWS API requests. For code samples, see Example projects in AWS samples repository