I'm trying (according to documentation https://learn.microsoft.com/en-us/azure/azure-signalr/concept-upstream#signature) to validate signature in incoming upstream web hook requests of Azure SignalR service.
No matter what I tried, the signatures do not match. I must be doing something wrong, or the documentation is not describing the content from which a HMAC hash should be created correctly.
This is the code, unit test, that illustrate the problem. I have edited sensitive information, replacing it by "...".
using System.Security.Cryptography;
using System.Text;
public class SignatureTests
{
[Fact]
public void Validate_upstream_signature()
{
// Azure SignalR access keys copied from the portal UI.
const string primaryAccessKeyBase64 = "...";
const string secondaryAccessKeyBase64 = "...";
/*
sample upstream HTTP request from development environment:
x-asrs-connection-id: 2UxdekB_sH6fnBmyv2DpjA2k43BwM02
x-asrs-hub: acahub
x-asrs-category: connections
x-asrs-event: disconnected
x-asrs-user-id: ...
x-asrs-client-query: ?hub=acahub&id=zzlVXz6QpmnyenyyIiAyiA2k43BwM02&access_token=...
x-asrs-signature: sha256=8abbda025556d4606bead7c432ad53881e66b11f274d6083560eb623f93c50d5, sha256=3e8fc2f9cf42b3346dfc1574c7be6647194c75d98fa1fcae76855df27c270c79
*/
const string signatureHeaderValue = "sha256=8abbda025556d4606bead7c432ad53881e66b11f274d6083560eb623f93c50d5, sha256=3e8fc2f9cf42b3346dfc1574c7be6647194c75d98fa1fcae76855df27c270c79";
const string connectionIdHeaderValue = "2UxdekB_sH6fnBmyv2DpjA2k43BwM02";
var primaryAccessKey = Convert.FromBase64String(primaryAccessKeyBase64);
var secondaryAccessKey = Convert.FromBase64String(secondaryAccessKeyBase64);
var (primaryHex, secondaryHex) = ExtractSignatures(signatureHeaderValue);
var primarySignature = Convert.FromHexString(primaryHex);
var secondarySignature = Convert.FromHexString(secondaryHex);
var content = Encoding.UTF8.GetBytes(connectionIdHeaderValue);
var primaryOk = Verify(primaryAccessKey, content, primarySignature);
var secondaryOk = Verify(secondaryAccessKey, content, secondarySignature);
Assert.True(primaryOk);
Assert.True(secondaryOk);
}
public static (string primary, string secondary) ExtractSignatures(string signatureHeaderValue)
{
var parts = signatureHeaderValue.Split(",", StringSplitOptions.RemoveEmptyEntries);
var primary = parts[0].Trim()[7..];
var secondary = parts[1].Trim()[7..];
return (primary, secondary);
}
public static bool Verify(byte[] accessKey, byte[] content, byte[] signature)
{
using var hmac = new HMACSHA256(accessKey);
var hash = hmac.ComputeHash(content);
return CryptographicOperations.FixedTimeEquals(signature, hash);
}
}