HTTP Request signing
To verify that requests have been made by a trusted source and have not been altered by third parties, requests shall be signed. This page explains how request shall be signed.
The following standard is applied: https://tools.ietf.org/html/draft-cavage-http-signatures-10
The certificate
The certificate that shall be used for certification, is generated by Invers B.V. and shall be sent as a part of the onboarding procedure. Only the public certificate is registered in Invers' system to validate signatures.
Headers
To sign a request, following headers are required:
- ApiKey
- X-Request-ID
- Date
- Digest
- Signature
ApiKey
The ApiKey sent by Invers B.V. during onboarding.
ApiKey header example
ApiKey: cEZrSmVPLTN1XzVDM09nVDhEanlZaUJwYzRXTldpVUc=
X-Request-ID
X-Request-ID shall be a Guid. Note that the Guid must be unique for each request.
X-Request-ID header example
X-Request-ID: f1b8d9bd-0118-47ff-bdb7-5e2956ad0e9f
Date
Request date and shall contain the current date, in HTTP Date (RFC 2616).
DateTime.Now.ToUniversalTime().ToString("r");
Date header example
Date: Wed, 25 Sep 2019 07:45:19 GMT
Digest
Contains a hash of the requestbody, more information on this can be found below.
Digest header example
Digest: sha-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==
Signature
Contains the signature and signature info, more information on generating a signature header can be found below.
Signature header 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))
- The value that should be hashed is the request body, if said body is empty then the empty string must be hashed.
- Both SHA-512 and SHA-256 can be used as hashing algorithm
- The output must be Base64 (RFC 4648) encoded.
- The value must be added to the digest-header.
A few examples:
Digest: sha-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
Digest: sha-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==
C# Example code for 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
- The order of the headers in the signing string is important.
- The names of the headers must be lowercase.
- Note: date format: In C# this will be:
DateTime.Now.ToUniversalTime().ToSTring("r")
. - Expected header: date, digest and x-request-id.
Signing string 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 of the construction of 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;
}
Generating the signature
The signature is the signature string and signed using the certificate: Base64(RSA-SHA512(signing_string))
- As algorithm RSA-SHA512 must be used
- The private key of the certificate shall be used
- The output shall be Base64 (RFC 4648) encoded.
An example of a signature:
lvJxQyr9JTBZGj5zIss4lPRDntClh9wdYRdcq1MQYd0sdav/ccPR3tyEWklpgGc9vLNLETLUnAXl4LtmSEW6Zu4ZHxlJ82Y7hzzjDYDnyj1OSZa97ArlY9N5aLKKG1rICPTeWPN4hxID/taThTh8Ab2TCCNrpYRiIYTkxHWvjrQF8HPerbFalqjLn1WfNyhCIGkO6u6I2H9hOsLaFJG3k+Ui5i3IxmaT1yLSWYgHxo32xRHaGr6KqrspkbQmoCVbI78SkCkajOuBhVUXqAw213GuOxjLaOFFnoLAo3cfrMxvPtL/2JMpTjYUPdTZJ3EsuXXnvx7bwmrvooJtuNRgFw==
C# Example of generating a signature using the signing string
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 contains the following sections:
- keyId - ApiKey.
- algorithm - The value of the algorithm, this shall be
rsa-sha512
. - headers - Lowercase list of the headers that have been used in the signature. The value shall be
date digest x-request-id
- signature - The value calculated in the previous section.
keyId="cEZrSmVPLTN1XzVDM09nVDhEanlZaUJwYzRXTldpVUc=",algorithm="rsa-sha512",headers="date digest x-request-id",signature="lvJxQyr9JTBZGj5zIss4lPRDntClh9wdYRdcq1MQYd0sdav/ccPR3tyEWklpgGc9vLNLETLUnAXl4LtmSEW6Zu4ZHxlJ82Y7hzzjDYDnyj1OSZa97ArlY9N5aLKKG1rICPTeWPN4hxID/taThTh8Ab2TCCNrpYRiIYTkxHWvjrQF8HPerbFalqjLn1WfNyhCIGkO6u6I2H9hOsLaFJG3k+Ui5i3IxmaT1yLSWYgHxo32xRHaGr6KqrspkbQmoCVbI78SkCkajOuBhVUXqAw213GuOxjLaOFFnoLAo3cfrMxvPtL/2JMpTjYUPdTZJ3EsuXXnvx7bwmrvooJtuNRgFw=="
C# example of generating a 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);
}