Signing requests




You must secure all API requests to Amazon Pay by signing them with your private key. The signature is used to verify the identity of the requestor and protect the data during transit. Follow these steps to sign a request:

Step 1: Generate a canonical request
Arrange the contents of your request (host, action, headers, etc.) into a standard (canonical) format.

Step 2: Create a String to Sign
Create a string to sign by concatenating the hashing algorithm designation (AMZN-PAY-RSASSA-PSS-V2) and the digest (hash) of the canonical request.

Step 3: Calculate the Signature
Sign the string to sign using RSASSA-PSS algorithm with SHA256 hashing and then Base64 encode the result.

Step 4: Add the Signature to the HTTP Request
After you calculate the signature, add it as a request header.

Step 1. Generate a canonical request

Create a string that includes information from your request in a standardized (canonical) format.

Example canonical request pseudo-code:

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

Example request:

POST
/live/v1/checkoutSessions

accept:application/json
content-type:application/json
x-amz-pay-date:20190923T231908Z
x-amz-pay-host:pay-api.amazon.com
x-amz-pay-idempotency-key:cllHyiNvS8cJ8Zas
x-amz-pay-region:na

accept;content-type;x-amz-pay-date;x-amz-pay-host;x-amz-pay-idempotency-key;x-amz-pay-region
0b6c19dc5bc1883ebd68d3c77ee929922c6b4a59e0a506d96c45e0c024c3295b

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.), followed by a newline character.

2. Add the canonical URI, followed by a newline character. The canonical URI is everything in the URI from the HTTP host to the question mark character ("?") before the query string parameters (if any). Normalize URI paths according to RFC 3986. Remove redundant and relative path components. Each path segment must be URI-encoded.

3. Add the canonical query string, followed by a newline character. If the request does not include a query string, use an empty string (essentially, a blank line). To construct the canonical query string:

  1. Sort the parameter names by character code point in ascending order. For example, a parameter name that begins with the uppercase letter F precedes a parameter name that begins with a lowercase letter b.
  2. URI-encode each parameter name and value according to the following rules:
    • Do not URI-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 (not using '+', since some encoding schemes do), and extended UTF-8 characters must be in the form %XY%ZA%BC.
  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 equals sign character (=), followed by the URI-encoded parameter value. Use an empty string for parameters that have no value.
  5. Append the ampersand character (&) after each parameter value, except for the last value in the list.

4. Add the canonical headers, followed by a newline character. The canonical headers is a list of all the HTTP headers that you are including with the signed request. See API introduction for mandatory headers. To create the canonical headers list:

  1. Convert all header names to lowercase, remove leading spaces and trailing spaces, and convert sequential spaces in the header value to a single space.
  2. Sort headers by character code
  3. Iterate through header names and construct each header entry using the follow rules:
    • Append the lowercase header name followed by a colon.
    • Append a comma-separated list of values for that header.
    • Do not sort the values in headers that have multiple values. Append a new line ('\n').

Pseudo-code for constructing the canonical list of headers:

CanonicalHeadersEntry0 = Lowercase(HeaderName) + ':' + Trim(HeaderValue) + '\n'
CanonicalHeaders = CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ... + CanonicalHeadersEntryN

Lowercase represents a function that converts all characters to lowercase. Trim removes excess white space before and after values, and converts sequential spaces to a single space.

5. Add the signed headers, followed by a newline character. This value is the list of headers that you included in the canonical headers.

6. Use SHA256 to create a hash of the HTTPS request body. The hashed payload must be represented as a lowercase hexadecimal string.

7. Construct the finished canonical request by combining components from each step into a single string. Note that each component except for the last component ends with a newline character.

Step 2. Create a String to Sign

Psuedo-code structure of string to sign:

StringToSign =
    Algorithm + \n +
    LowerCase(Hex(Hash(CanonicalRequest)))

Example string to sign:

AMZN-PAY-RSASSA-PSS-V2 
c5c55b2d523738b72c0b96f6d5e0d712d9496573490125b191eeb6840c052ffb

To create the string to sign, concatenate the algorithm and digest of the canonical request:

1. Start with the algorithm designation, followed by a newline character. This value is the hashing algorithm that you use to calculate the digests in the canonical request:

AMZN-PAY-RSASSA-PSS-V2 

2. Append SHA256 hash of the canonical request created in the previous step. This value is not followed by a newline character. The hashed canonical request must be lowercase base-16 encoded, as defined by Section 8 of RFC 4648:

f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59

Step 3. Calculate the Signature

To calculate the signature, sign the string to sign created in Step 2 with your private key. Use RSASSA-PSS algorithm with SHA256 hashing and a salt length of 20. Complete this step by Base64 encoding the result.

Pseudo-code for calculating the signature:

signature = RSASSA-PSS(privateKey, StringToSign);
base64_encoded_signature = Base64Encode(signature);

Calculated signature example:

o/THnkmKYB+jCPOo3Qchux3T/dKmOqLTiL2MMmfshGIgATo3206BlJVjAIqL8LhuEu9c70conBoMsfc5ZWvdijTQ893fIXA7WjEKFqTwyBOQz2EIjep7f8Yf4g5bgWzKLJYK3ABTptoND96yjo3IZVTQLHW+G2FQs8X/lSW8XUjHgobcSUKF4X2/3ukZQ+QEtw5vLZcpzcvyhdwBZakgqcXQRuaryS3iekZXUsdajOd+pMMSOw5yQXLE3qXukxiWXNcDnc3bCw+8JtynfcVYUX+Q3Z6/J0p9nODmfNAuvhTgpfnFpXKRUTGuhANiOyOSN8orWSx1MVPPwHSyZhhpPA==

Step 4. Add the Signature to the HTTP request

After you calculate the signature, add it to the request as an HTTP header named Authorization in this format: Algorithm + PublicKeyId + SignedHeaders + Signature.

  • Algorithm will always be AMZN-PAY-RSASSA-PSS-V2
  • PublicKeyId is the credential provided by Amazon Pay. See Get you publicKeyId for more info.
  • SignedHeader is a list of all headers included in the signature. Note that the Authorization header should not be included in the list of signed headers.
  • Signature is the value calculated in Step 3.

Pseudo-code for constructing the header:

Authorization: algorithm PublicKeyId=publicKeyId, SignedHeaders=x-amz-pay-date, Signature=base64_encoded_signature

Finished Authorization header example:

Authorization: AMZN-PAY-RSASSA-PSS-V2 PublicKeyId=AHEGSJCM3L2S637RBGABLAFW, SignedHeaders=accept;content-type;x-amz-pay-date;x-amz-pay-host;x-amz-pay-idempotency-key;x-amz-pay-region, Signature=o/THnkmKYB+jCPOo3Qchux3T/dKmOqLTiL2MMmfshGIgATo3206BlJVjAIqL8LhuEu9c70conBoMsfc5ZWvdijTQ893fIXA7WjEKFqTwyBOQz2EIjep7f8Yf4g5bgWzKLJYK3ABTptoND96yjo3IZVTQLHW+G2FQs8X/lSW8XUjHgobcSUKF4X2/3ukZQ+QEtw5vLZcpzcvyhdwBZakgqcXQRuaryS3iekZXUsdajOd+pMMSOw5yQXLE3qXukxiWXNcDnc3bCw+8JtynfcVYUX+Q3Z6/J0p9nODmfNAuvhTgpfnFpXKRUTGuhANiOyOSN8orWSx1MVPPwHSyZhhpPA==