HMAC
Overview
Using the HTTP Authorization header is the most common method of providing authentication information. The header value contains the algorithm and few other information that is used to calculate the signature. Below is the example of an Authorization header value. Please note that line breaks have been added to this example for readability.
x-icims-v1-hmac-sha256 user=testuser,
signedheaders=host;x-icims-date,
signature=bcb37825f5d7e08e222da05cc0776355e3c69f94144d754a75326b8a4b2cde73
Note there is a space between the first two components, x-icims-v1-hmac-sha256 and user, and that the subsequent components, signedheaders and signature, are separated by comma.
The following table describes the various components of the Authorization header value.
Component | Description |
---|---|
x-icims-v1-hmac-sha256 | The algorithm that is used to calculate the signature. The string specifies iCIMS signature version 1 (x-icims-v1) and the signing algorithm HMAC-SHA256. |
user | Username of the integration user sending the request. |
signedheaders |
A semicolon-separated list of requested headers that you used to compute the Signature. The list includes header names only. The header names must be in lowercase. For example:
|
signature |
The 256-bit signature expressed as 64 lowercase hexadecimal characters. For example:
|
Upon receiving the request, iCIMS recreates the string to sign using the information in the Authorization header and the date header. It then verifies whether the calculated signature matches the signature in the Authorization header. The request date can be specified by using the x-icims-date header.
Calculating a Signature
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 diagram below illustrates this process. (The computation of various components of the string that you create for signing is denoted in subsequent sections.)
When iCIMS receives a request, it computes its own signature for the request and compares it to the signature provided in the request. Because of this, both ends (sender and receiver) should use the same method for signature computation.
1. Canonical Request
The first step for computing a signature is to create a string that includes the request information in standard (canonical) format. The steps below indicate the steps to create a canonical version of the request.
<HttpMethod>\n
<CanonicalURI>\n
<CanonicalQueryString>\n
<CanonicalHeaders>\n
<SignedHeaders>
To better understand the construction of the canonical form of HTTP or HTTPS request, review the following sample request:
POST https://api.icims.com/people HTTP/1.1
Host: api.icims.com
Content-Type: application/json
X-Icims-Date: 2014-09-03T15:23+0000
X-Icims-Content-SHA256: 2d911cf32ef8c5e9de94c79edf62f2fec33091a7cd8c561bc9d19623b0146ce4
{“firstname”:“abc”,“lastname”:”xyz”,”email”:”abcxyz@icims.com”}
-
HTTPMethod: one of the HTTP methods, such as GET, POST, PUT, etc.
POST
-
CanonicalURI: the URI-encoded version of the absolute path component of the URI (i.e., everything starting with the “/” that follows the domain name and up to the end of string or to the question mark character (‘?’), if you have query string parameters). So in URI https://api.icims.com/people, /people is the absolute path. URI paths should be normalized based on RFC 3986 by removing redundant and relative path components. If the absolute path is empty, use a forward slash (/).
/people
-
CanonicalQueryString: specifies the query string parameters. Each parameter name and value should be URI-encoded individually. After encoding the name and value, all of the parameters should be sorted based on parameter name before creating the canonical query string.
The steps to compute the canonical query string are as follows:
- 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 (not using '+', as some encoding schemes do), asterisk as %2A and not as ‘*’. Extended UTF-8 characters must be in the form %XY%ZA%BC.
- 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). If the parameter names are equals, sort based on value
- Build the canonical query string by starting with the first parameter name in the sorted list.
- For each parameter, append the URI-encoded parameter name, followed by the character '=' (ASCII code 61), and then followed by the URI-encoded parameter value. (Use an empty string for parameters that have no value.)
-
Append the character '&' (ASCII code 38) after each parameter value except for the last value in the list.
For example, in the URL:
https://api.icims.com/people?lastname=xyz&firstname=abc
The query string is lastname=xyz&firstname=abc. The canonical query string is as follows:
URI-encode(“firstname”) + “=” + URI-encode(“abc”) + “&” + URI-encode(“lastname”) + “=” + URI-encode(“xyz”)
Note: If the request does not include a query string, set this value in the canonical query to an empty string.
-
CanonicalHeaders: a list of request header entries, sorted by lowercased header names. Each header entry is computed using lowercased header name and trimmed value, separated by a colon and followed by a newline character, as shown below:
Lowercase(<HeaderName>)
+":"+Trim(<value> )+"\n" If there are multiple headers with the same header name, all the header values should be combined into single value. The single value can be obtained by first sorting the values and then combining them, each separated by comma. For example,
Content-Disposition: test.doc
Content-Disposition: attachement; filename=testfileis changed to
Content-Disposition: attachement; filename=testfile,test.doc
The CanonicalHeaders list must include the following headers:
-
X-Icims-Date, indicating the timestamp when the request was made. iCIMS accepts date-time in the following format, YYYY-MM-DDThh:mm:ssTZD. All requests older than 5 minutes will be rejected.
- YYYY: four digit year
- MM: two-digit month (01 through 12)
- DD: two-digit day of month (01 through 31)
- hh: two digits of hour (00 through 23; am/pm not allowed)
- mm: two digits of minute (00 through 59)
- ss: two digits of second (00 through 59)
- TZD: time zone designator (Z or +hh:mm or –hh:mm)
- X-Icims-Content-SHA256, indicating the SHA256 hash of the request payload. If the payload is empty, send the SHA256 hash of empty string.
Note: Newline character is required even for the last header.
For the example introduced above, the following CanonicalHeaders would be included:
content-type:application/json
host:api.icims.com
x-icims-content-sha256: 2d911cf32ef8c5e9de94c79edf62f2fec33091a7cd8c561bc9d19623b0146ce4
x-icims-date:2014-09-03T15:23+0000 -
X-Icims-Date, indicating the timestamp when the request was made. iCIMS accepts date-time in the following format, YYYY-MM-DDThh:mm:ssTZD. All requests older than 5 minutes will be rejected.
-
SignedHeaders: alphabetically sorted, semicolon-separated list of lowercase request header names. The request headers in this list are the request headers that we included in the CanonicalHeaders string. This string should only contain the headers that were used to calculate the canonical headers string. This list is also send as part of the Authorization Header as discussed below in Authorization Header section of this page.
For the example above, the SignedHeaders string would look like:
content-type;host;x-icims-content-sha256;x-icims-date
So the resulting CanonicalRequest string for the example request would look like:
POST
/people
content-type:application/json
host:api.icims.com
x-icims-content-sha256:2d911cf32ef8c5e9de94c79edf62f2fec33091a7cd8c561bc9d19623b0146ce4
x-icims-date:2014-09-03T15:23+0000
content-type;host;x-icims-content-sha256;x-icims-date
2. StringToSign
The second step is to create a string to sign. This string contains the meta information about the request and the canonical request string created above in Step 1: Canonical Request.
x-icims-v1-hmac-sha256 + “\n” +
TimeStamp + “\n” +
HEX (SHA256Hash (CanonicalRequest))
-
Start with the algorithm used to calculate the digests in the canonical request. (ie., x-icms-hmac-sha256)
x-icims-v1-hmac-sha256\n
-
Append the TimeStamp when the request was initiated. This is the value of the header x-icims-date in ISO 8601 format.
2014-09-03T15:23+0000\n
-
Append the hash of the canonical request string you created in Step 1. Note that this value is not followed by newline character. The hash should be lowercased base-16 encoded.
fc9f4e23ef1b2584106a1187f95c95618439ae0d090605c5526abb3878fce0dc
The following is the StringToSign for the example request introduced above:
x-icims-v1-hmac-sha256
2014-09-03T15:23+0000
fc9f4e23ef1b2584106a1187f95c95618439ae0d090605c5526abb3878fce0dc
3. Calculating the Signature
The last step is to sign the string created in Step 2: StringToSign using the shared secret key and the HMAC-SHA256 algorithm defined in RCF 2104.
Signature = HMAC-SHA256 (SigningSecret, StringToSign)
For example, using the test secret key “wbVAAhyNDxK8kU/dk0qyd1g6hzmGtkZc8j6tB112J0c=” to sign the above StringToSign using HMAC-SHA256 algorithm would result in the following signature:
0e8ca243f3a0ba75d47d906adbc9e2e4abe68877d406944d5a4dc4635e7a3a20
Authorization Header
After completing the 3 steps described above (1. Canonical Request, 2. StringToSign, and 3. Calculating the Signature), the Authorization header value is generated, as shown below.
x-icms-v1-hmac-sha256 user=
, signedheaders= , signature=
- The user is the username/login id of the user that is making this request.
- SignedHeaders is the signed headers string that was computed above.
- Signature is the final signature computed using the procedure above.
The Final Authorization header value for the example above would be:
x-icims-v1-hmac-sha256 user=testuser,signedheaders=content-type;host;x-icims-content-sha256;x-icims-date,signature= 0e8ca243f3a0ba75d47d906adbc9e2e4abe68877d406944d5a4dc4635e7a3a20
This Authorization header should be included in the request. The receiving end should use the same procedure described above to calculate its own Signature and verify it with the signature present in the authorization header to validate the authenticity.