Sharing a secrect with GitHub, it is important to verify the signaure hashed by it. You don't want your API being called by malicious 3rd parties.
Here's how you do it in ASP.NET Core WebAPI.
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Primitives;
const string secret = "this is the secret shared with github webhooks for signing"; // Only your service and github knows about it.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Assuming GitHub will POST on /payloads.
app.MapPost("/payloads", async (HttpContext httpContext, CancellationToken cancellationToken) =>
{
string? payload = null;
using (var reader = new StreamReader(httpContext.Request.Body, leaveOpen: true))
{
payload = await reader.ReadToEndAsync();
}
Console.WriteLine("I got some json: " + payload);
httpContext.Request.Headers.TryGetValue("x-hub-signature-256", out StringValues signatureSHA256);
Console.WriteLine("And the signature: " + signatureSHA256);
// Verify header here if you want.
// Re-compute the hash value using the secret as the key.
byte[] key = Encoding.UTF8.GetBytes(secret);
using (HMACSHA256 hmac = new HMACSHA256(key))
{
{
// Use secret to re-compute the hash of the payload.
byte[] computedSig = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
// Notice the hash needs to be in HEX before comparison
Console.WriteLine("Computed signature: {0}", ToHexString(computedSig));
}
}
});
static string ToHexString(byte[] bytes)
{
var builder = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
{
builder.AppendFormat("{0:x2}", b);
}
return builder.ToString();
}
app.Run();
Result:
I got some json: {"zen":"Avoid administrative distraction.","hook_id":395209045,"hook":{json_content}
And the signature: sha256=71d8e307d216a45425b57220f4ed89c1111b1f410213fec83659e8d4c1253813 # comes from the header
Computed signature: 71d8e307d216a45425b57220f4ed89c1111b1f410213fec83659e8d4c1253813 # computed with the secret