HMAC Validation (ASP.NET Core)

Adding HMAC (Hash-based Message Authentication Code) authentication to an ASP.NET Core application can help ensure that requests are authenticated and have not been tampered with. You can implement this as a custom middleware.

The Middleware Class

The middleware will extract the necessary information from the request headers (e.g., Authorization header), calculate the HMAC signature, and compare it to the one provided by the client.

using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Primitives;

public class HmacAuthenticationMiddleware(RequestDelegate next, string secretKey)
{
    private readonly RequestDelegate next = next;
    private readonly string secretKey = secretKey;

    public async Task Invoke(HttpContext context)
    {
        // Extract the HMAC signature from the Authorization header
        if (!context.Request.Headers.TryGetValue("Authorization", out StringValues authHeader))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Missing Authorization Header");
            return;
        }

        // The Authorization header should be in the format: HMAC <signature>
        string[] authParts = authHeader.ToString().Split(' ');
        if (authParts.Length != 2 || authParts[0] != "HMAC")
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Invalid Authorization Header Format");
            return;
        }

        string providedSignature = authParts[1];

        // Calculate the HMAC of the request
        string calculatedSignature = await ComputeHmacSignature(context, secretKey);

        // Compare the provided signature with the calculated one
        if (!providedSignature.Equals(calculatedSignature, StringComparison.OrdinalIgnoreCase))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Invalid Signature");
            return;
        }

        // Call the next middleware in the pipeline
        await next(context);
    }

    private static async Task<string> ComputeHmacSignature(HttpContext context, string secretKey)
    {
        // Read the request content
        context.Request.EnableBuffering();
        var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
        context.Request.Body.Position = 0;

        // Compute the HMAC SHA256 hash
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
        byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
        return Convert.ToBase64String(hash);
    }
}

Extension Method for Middleware

n .NET, extension methods for middleware registration encapsulate setup details, simplifying Startup.cs and promoting clean, reusable code. This convention hides complexity, providing a fluent and consistent way to add middleware to the request pipeline.

public static class HmacAuthenticationMiddlewareExtensions
{
    public static IApplicationBuilder UseHmacAuthentication(this IApplicationBuilder app, string secretKey)
    {
        ArgumentNullException.ThrowIfNull(app);

        return app.UseMiddleware<HmacAuthenticationMiddleware>(secretKey);
    }
}

Register the Middleware

Register the middleware in your Program.cs or Startup.cs file. Use the HMAC secret key generated by Cubu.

app.UseHmacAuthentication("<HMAC secret key>");

Last updated