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); } } }