namespace Intuto.WebhookSignatureClient
{
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
public class WebhookSignatureClient
{
///
/// Key: tenantId
/// Value: WebhookSigningKey
///
private readonly IDictionary _webhookSigningKeyDict;
public WebhookSignatureClient(Dictionary signingKeys)
{
_webhookSigningKeyDict = signingKeys;
}
///
/// Hash Request Body using SHA256
///
/// Base64 encoded string
private static string ComputeContentHash(string content)
{
using (var sha256 = SHA256.Create())
{
var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(content));
return Convert.ToBase64String(hashedBytes);
}
}
///
/// Sign a Base64 Array with a given key using HMACSHA256
///
/// Base64 encoded signed string
private static string ComputeSignature(string stringToSign, Guid webhookSigningKey)
{
using (var cryptographer = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSigningKey.ToString())))
{
var hashedBytes = cryptographer.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
return Convert.ToBase64String(hashedBytes);
}
}
///
/// Generate signature based on the request inputs
///
private string GenerateRequestSignature
(
string method,
string path,
string date,
string json,
Guid webhookSigningKey
)
{
var contentHash = ComputeContentHash(json);
var stringToSign = $"METHOD:{method};HOST:{path};DATE:{date};HASH:{contentHash};";
return ComputeSignature(stringToSign, webhookSigningKey);
}
///
/// Validate Intuto Webhook Request
///
/// Request method (is always POST)
/// Request Path
/// Date header on the request
/// x-intuto-tenant header on the request (or TenantId from the request body)
/// x-intuto-signature header on the request
/// Raw JSON Request Body
public bool ValidateSignature
(
string method,
string path,
string date,
int tenantId,
string signature,
string json
)
{
// lookup stored signing key from storage based on tenantId
var webhookSigningKey = _webhookSigningKeyDict[tenantId];
// compute our signature
var computedSignature = GenerateRequestSignature
(
method,
path,
date,
json,
webhookSigningKey
);
// check signatures match
return signature.Equals(computedSignature);
}
}
}