HTTP Request signing
To ensure the integrity and authenticity of API requests, all requests must be signed. This process confirms that the API requests originate from a trusted source and have not been tampered with during transmission.
This guide explains how to sign HTTP requests according to the HTTP Signatures standard.
Certificate
The certificate used for signing requests is provided by Invers B.V. during the onboarding process. Only the public certificate is registered with Invers to validate signatures.
Required Headers
To sign a request, the following headers are required:
Header | Type | Description |
---|---|---|
ApiKey | String | API key issued by Invers B.V. during onboarding. |
x-request-id | Guid | A unique GUID for each request. |
date | String | The current date in HTTP Date format (RFC 2616). |
digest | String | Base64 encoded hash of the request body. |
Signature | String | Contains the signed string with details of the signature method and key. |
ApiKey
The ApiKey
is issued by Invers B.V. during onboarding.
Example:
ApiKey: cEZrSmVPLTN1XzVDM09nVDhEanlZaUJwYzRXTldpVUc=
X-Request-ID
The x-request-id
must be a unique GUID for each request.
Example:
X-Request-ID: f1b8d9bd-0118-47ff-bdb7-5e2956ad0e9f
Date
The date
header should contain the current date in HTTP Date format (RFC 2616).
DateTime.Now.ToUniversalTime().ToString("r");
Example:
Date: Wed, 25 Sep 2019 07:45:19 GMT
Digest
The digest
header ensures the integrity of the request body. It is generated by hashing the request body using SHA-256 or SHA-512 and then encoding the result in Base64.
- If the body is empty, hash an empty string.
- The output is Base64 (RFC 4648) encoded.
Example:
Digest: sha-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==
Signature
The Signature
header contains the signed string and additional information. The signature is created using RSA-SHA512.
Example:
Signature: keyId="cEZrSmVPLTN1XzVDM09nVDhEanlZaUJwYzRXTldpVUc=",algorithm="rsa-sha512",headers="date digest x-request-id",signature="lvJxQyr9JTBZGj5zIss4lPRDntClh9wdYRdcq1MQYd0sdav/ccPR3tyEWklpgGc9vLNLETLUnAXl4LtmSEW6Zu4ZHxlJ82Y7hzzjDYDnyj1OSZa97ArlY9N5aLKKG1rICPTeWPN4hxID/taThTh8Ab2TCCNrpYRiIYTkxHWvjrQF8HPerbFalqjLn1WfNyhCIGkO6u6I2H9hOsLaFJG3k+Ui5i3IxmaT1yLSWYgHxo32xRHaGr6KqrspkbQmoCVbI78SkCkajOuBhVUXqAw213GuOxjLaOFFnoLAo3cfrMxvPtL/2JMpTjYUPdTZJ3EsuXXnvx7bwmrvooJtuNRgFw=="
Generating a Digest
The digest
is a Base64 (RFC 4648) encoded hash of the request body: Base64(SHA512(body))
- If the request body is empty, hash the empty string.
- You can use either SHA-256 or SHA-512 as the hashing algorithm.
- The output must be Base64 encoded and added to the
digest
header.
Examples:
Digest: sha-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
Digest: sha-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==
C# Example: Generating the Digest
private string CalculateDigest(byte[] body, HashAlgorithmName hashAlgorithm)
{
byte[] hash = null;
using (var algorithm = HashAlgorithm.Create(hashAlgorithm.Name))
{
hash = algorithm.ComputeHash(body);
}
return Convert.ToBase64String(hash);
}
Generating the Signing String
To generate the signing string, include the following headers in order:
date
digest
x-request-id
Ensure that all header names are in lowercase and separated by newline characters.
The date
header must be formatted correctly.
In C#, use DateTime.Now.ToUniversalTime().ToString("r") to produce the required format.
Example:
date: Wed, 25 Sep 2019 07:45:19 GMT
digest: sha-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==
x-request-id: 23bfabd8-3ffa-4e41-a851-2395f15a889e
C# Example: Constructing a Signing String
private string CalculateSigningString(string requestDate, string requestDigest, string requestId)
{
var s = string.Format("date: {0}\ndigest: {1}\nx-request-id: {2}", requestDate, requestDigest, requestId);
return s;
}
Use \n
for a new line.
Generating the Signature
The signature is the signature string and signed using the certificate: Base64(RSA-SHA512(signing_string))
- Create the signing string using the required headers.
- Sign the string using your private key with the RSA-SHA512 algorithm.
- Encode the resulting signature in Base64 (RFC 4648).
Example:
lvJxQyr9JTBZGj5zIss4lPRDntClh9wdYRdcq1MQYd0sdav/ccPR3tyEWklpgGc9vLNLETLUnAXl4LtmSEW6Zu4ZHxlJ82Y7hzzjDYDnyj1OSZa97ArlY9N5aLKKG1rICPTeWPN4hxID/taThTh8Ab2TCCNrpYRiIYTkxHWvjrQF8HPerbFalqjLn1WfNyhCIGkO6u6I2H9hOsLaFJG3k+Ui5i3IxmaT1yLSWYgHxo32xRHaGr6KqrspkbQmoCVbI78SkCkajOuBhVUXqAw213GuOxjLaOFFnoLAo3cfrMxvPtL/2JMpTjYUPdTZJ3EsuXXnvx7bwmrvooJtuNRgFw==
C# Example: Generating a Signature
private string CalculateSignature(string signingString)
{
if (_certificate != null)
{
byte[] bytesData = Encoding.ASCII.GetBytes(signingString);
byte[] bytesEncrypted = _certificate.GetRSAPrivateKey().SignData(bytesData, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(bytesEncrypted);
}
else
{
_logger.LogError("No certificate loaded for calculating signature");
}
return String.Empty;
}
Constructing the Signature Header
The Signature
header includes the following components:
keyId
: TheApiKey
.algorithm
- The value of the algorithm, which should bersa-sha512
.headers
- Lowercase list of the headers that have been used in the signature. The value shall bedate digest x-request-id
signature
- The generated signature.
Example:
keyId="cEZrSmVPLTN1XzVDM09nVDhEanlZaUJwYzRXTldpVUc=",algorithm="rsa-sha512",headers="date digest x-request-id",signature="lvJxQyr9JTBZGj5zIss4lPRDntClh9wdYRdcq1MQYd0sdav/ccPR3tyEWklpgGc9vLNLETLUnAXl4LtmSEW6Zu4ZHxlJ82Y7hzzjDYDnyj1OSZa97ArlY9N5aLKKG1rICPTeWPN4hxID/taThTh8Ab2TCCNrpYRiIYTkxHWvjrQF8HPerbFalqjLn1WfNyhCIGkO6u6I2H9hOsLaFJG3k+Ui5i3IxmaT1yLSWYgHxo32xRHaGr6KqrspkbQmoCVbI78SkCkajOuBhVUXqAw213GuOxjLaOFFnoLAo3cfrMxvPtL/2JMpTjYUPdTZJ3EsuXXnvx7bwmrvooJtuNRgFw=="
C# Example: Generating the Signature Header
private string GenerateSignatureHeader(string apiKey, string requestSignature)
{
return string.Format("keyId=\"{0}\",algorithm=\"rsa-sha512\",headers=\"date digest x-request-id\",signature=\"{1}\"", apiKey, requestSignature);
}