Step 1: Create a Canonical Request
To begin the signing process, create a string that includes information from the request in a standardized (canonical) format. This ensures that when the IRB Exchange API receives the request, it can calculate the same signature.
Follow the steps here to create a canonical version of the request. Otherwise, the version calculated and the version calculated by the IRB Exchange will not match, and the request will be denied.
The following shows the pseudocode to create a canonical request.
CanonicalRequest =
HTTPRequestMethod + '\n' +
CanonicalURI + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' +
SignedHeaders + '\n' +
HexEncode(Hash(RequestPayload))
In this pseudocode, Hash
represents a function that produces a message digest, typically SHA-256. See the cryptography section for a list of supported hash algorithms. HexEncode
represents a function that returns the base-16 encoding of the digest in lowercase characters. For example, HexEncode("j")
returns the value 6a
rather than 6A
. Each input byte must be represented as exactly two hexadecimal characters.
The following examples show how to construct the canonical form of a request. The original request might look like this as it is sent from the client to the IRB Exchange API, except that this example does not include the signing information yet.
Example
GET https://irbexchange.huronsoftware.com/organizations?name=Huron HTTP/1.1
Host: irbexchange.huronsoftware.com
Huron-IrbX-Date: 20170227T054205Z
Huron-Irbx-Request-Id: 538ef29aa9b443a1be5642453dc15255
User-Agent: Huron.IrbExchange/1.0.0
The preceding example request is a GET
request to makes a list organizations call to the IRB Exchange API. The request takes the name
parameter to filter the result set.
To create a canonical request, concatenate the following components from each step into a single string:
Request Method
Start with the HTTP request method (GET, PUT, POST, etc.), followed by a newline character.
Example
GET
Canonical URI
Add the canonical URI parameter, followed by a newline character. The canonical URI is the URI-encoded version of the absolute path component of the URI, which is everything in the URI from the HTTP host to the question mark character ("?") that begins 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.
Example canonical URI with encoding
/documents%20and%20settings/
If the absolute path is empty, use a forward slash ("/") in the example request.
Example canonical URI
/organizations
Canonical Query String
Add the canonical query string, followed by a newline character. If the request does not include a query string, use an empty string. The example request has the following query string.
Example canonical query string
name=Huron
To construct the canonical query string, complete the following steps:
- Sort the parameter names by character code in acsending order. For example, a parameter that begins with the uppercase letter 'M' precedes a parameter name that begins with a lowercase letter 'd'.
- URI-encode each parameter name and value according to the following rules:
- Do not URI-encode any of the unreserved characters that RFC 3986 defined: A-Z, a-z, 0-9, hyphen (-), underscore (_), period (.), and tilde (~).
- Precent-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 %20 (not using '+', as some encoding schemes do) and extended UTF-8 characters must be in the form %XY%ZA%BC.
- 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 equal sign character (=), followed by the URI-encoded parameter value. Use an emptry string for parameters that have no value.
- Append the ampersand character (&) after each parameter value, except for the last value in the list.
Canonical Headers
Add the canonical headers, followed by a newline character. The canonical headers consist of a list of all the HTTP headers that you are including with the signed request.
At a minimum, you must include the Host
, Huron-IrbX-Date
, and Huron-IrbX-Request-Id
headers. Standard headers like Content-Type
are optional.
Example canonical headers
host:irbexchange.huronsoftare.com\n
huron-irbx-date:20170227T054205Z\n
huron-irbx-request-id:538ef29aa9b443a1be5642453dc15255\n
To create the canonical headers list, convert all header names to lowercase and remove leading spaces and trailing spaces. Convert sequential spaces in the header value to a single space.
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. The Trimall
function removes excess white space before and after values, and converts sequential spaces to a single space.
Build the canonical headers list by sorting the (lowercase) headers by character code and then iterating through the header names. Construct each header per the following rules:
- Append the lowercase header name followed by 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')
The following examples compare a complex set of headers with their canonical form:
Original headers
Host: irbexchange.huronsoftware.com
Content-Type: application/json; charset=utf-8
Header1: a b c
Huron-IrbX-Date: 20170227T054205Z
Huron-IrbX-Request-Id: 538ef29aa9b443a1be5642453dc15255
Header2: "a b c"
Canonical form
content-type:application/json; charset=utf-8\n
header1:a b c\n
header2:"a b c"\n
host:irbexchange.huronsoftware.com\n
huron-irbx-date:20170227T054205Z\n
huron-irbx-request-id:538ef29aa9b443a1be5642453dc15255\n
Signed Headers
Add the signed headers, followed by a newline character. This value is the list of headers that were included in the canonical headers. By adding this list of headers, you tell the IRB Exchange API which headers in the request are part of the signing process and which ones the IRB Exchange API can ignore for purposes of validating the request.
The Host
, Huron-IrbX-Date
, and Huron-IrbX-Request-Id
are required to be signed headers.
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.
Example signed headers
host;huron-irbx-date;huron-irbx-request-id\n
Hash Payload
Use a hash (digest) function like SHA-256to create a hashed value from the payload in the body of the HTTPS request.
Structure of payload
HashedPayload = Lowercase(HexEncode(Hash(requestPayload)))
If the payload is empty, use an empty string as the input to the hash function. In the example below, the payload is empty.
Example hashed payload (empty string)
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Canonical Request
To construct the finished canonical request, combine all the components from each step as a single string. As noted, each component ends with a newline character. If you follow the canonical request pseudocode explained above, the resulting canonical request is shown in the following example.
Example canonical request
GET\n
/organizations\n
name=Huron\n
host:irbexchange.huronsoftware.com\n
huron-irbx-date:20170227T054205Z\n
huron-irbx-request-id:538ef29aa9b443a1be5642453dc15255\n
host;huron-irbx-date;huron-irbx-request-id\n
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Hash Canonical Request
Create a hash (digest) of the canonical request with the same algorithm that was used to hash the payload. The hash canonical request is represented as a string of lowercase hexadecimal carachters. The following example shows the request of using SHA-256 to hash the example canonical request.
Example hashed canonical request
378bc8061ff7f431940ef5f51073bf01a85ddc01dedefee200c9bfb96f9460c9
You use the hashed canonical request and private key as the input to the encryption process to calculate the signature in Step 2.