I'm trying to implement HMAC security in a WCF web service, but am having a hard time verifying the HMAC signature. I am using .NET Framework 4.8.
I'm following DocuSign API documentation here:
https://developers.docusign.com/platform/webhooks/connect/validate/
Their documentation provides code samples, specifically in "Step 2: Verify the HMAC signature":
using System;
using System.Text;
using System.Security.Cryptography;
public static class HMACValidation
{
public static string ComputeHash(string secret, string payload)
{
byte[] bytes = Encoding.UTF8.GetBytes(secret);
HMAC hmac = new HMACSHA256(bytes);
bytes = Encoding.UTF8.GetBytes(payload);
return Convert.ToBase64String(hmac.ComputeHash(bytes));
}
public static bool HashIsValid(string secret, string payload, string verify)
{
ReadOnlySpan<byte> hashBytes = Convert.FromBase64String(ComputeHash(secret, payload));
ReadOnlySpan<byte> verifyBytes = Convert.FromBase64String(verify);
return CryptographicOperations.FixedTimeEquals(hashBytes, verifyBytes);
}
}
Per Microsoft, CryptographicOperations.FixedTimeEquals is only for: .NET Core 2.1, 2.2, 3.0, 3.1, .NET 5, .NET 6, .NET 7
So I'm unable to use this code sample in .NET Framework 4.8.
Question 1: in .NET Framework 4.8, is there another way to verify the HMAC signature?
I was unable to find an alternative, so I tried a similar approach to the Microsoft article below.
A Guide to Designing and Building RESTful Web Services with WCF 3.5
So, I implemented the following in my WCF service, in hopes to work-around the CryptographicOperations.FixedTimeEquals:
public string DocuSign(DocuSignWebhookRequest jsonRequest)
{
var payload = JsonConvert.SerializeObject(jsonRequest);
var secretKey = ConfigurationManager.AppSettings["HMAC_Key"];
var verifyKey = HttpContext.Current.Request.Headers["x-docusign-signature-1"];
var computedHashString = ComputeHash(secretKey, payload);
var valid = computedHashString.Equals(verifyKey); // false
}
private string ComputeHash(string secret, string payload)
{
byte[] bytes = Encoding.UTF8.GetBytes(secret);
HMAC Mac = new HMACSHA256(bytes);
bytes = Encoding.UTF8.GetBytes(payload);
return Convert.ToBase64String(Mac.ComputeHash(bytes));
}
Question 2: any ideas why my code is not able to verify the HMAC signature? It's always returning false when comparing to the verify key sent by DocuSign.
Here is an official document which shows how to sign a file by using the HMACSHA256 object and then how to verify the file: HMACSHA256 Class.
About the second question, the code you post have substituted in the actual value. I'm not sure if this will cause a problem.