HMAC Generator — C# source
Generate a keyed-hash HMAC (SHA-1/256/384/512) for a message and secret. Runs entirely in your browser via Web Crypto, with a shareable link to your exact input.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// hmac-generator — RFC 2104 keyed-hash HMAC of a UTF-8 message, hex output.
//
// Language: C# 12 (.NET 8, standard library only)
// Source: CosmoDev polyglot showcase port of the `hmac-generator` tool,
// ported from src/lib/hmac.ts (the canonical TypeScript lib).
// License: display source — part of CosmoDev's polyglot tool pages.
//
// Keyed HMAC via System.Security.Cryptography — the vetted, constant-time
// implementations .NET ships on every platform. The static HashData entry
// points (added in .NET 8) take the key first and compute a one-shot tag
// without the using/Dispose ceremony the older Create() pattern needed, and
// Convert.ToHexString supplies the lowercase hex the TypeScript reference
// produces. No NuGet packages are required.
//
// Behavior mirrors the TypeScript reference: UTF-8 inputs, lowercase hex
// output, SHA-256 by default, and rejection of empty secrets and unknown
// algorithms (ArgumentException — the C# standing of the Python port's
// ValueError and the two arms of the Rust port's HmacError enum). An empty
// secret is rejected because SubtleCrypto refuses a zero-length key;
// HMACSHA256.HashData itself would accept one, and matching the reference
// contract keeps the ports in parity. SHA-1 is offered for legacy
// compatibility only; it is not collision-resistant.
using System.Security.Cryptography;
using System.Text;
namespace CosmoDev.Hmac;
/// <summary>Pure logic of the CosmoDev `hmac-generator` tool.</summary>
public static class HmacTool
{
/// <summary>Canonical algorithm names. The spellings match the TypeScript
/// union so the same string works across every port.</summary>
public const string Sha1 = "SHA-1";
public const string Sha256 = "SHA-256"; // default algorithm
public const string Sha384 = "SHA-384";
public const string Sha512 = "SHA-512";
/// <summary>The algorithm selected when a caller passes an empty name —
/// the optional-parameter default the TypeScript reference declares.</summary>
public const string DefaultAlgorithm = Sha256;
/// <summary>
/// Compute HMAC(<paramref name="message"/>, <paramref name="secret"/>)
/// under the named algorithm and return it as lowercase hex.
///
/// Both inputs are UTF-8 encoded before hashing, so the result is correct
/// for arbitrary Unicode (emoji, accents, CJK). An empty
/// <paramref name="algorithm"/> selects SHA-256; an empty
/// <paramref name="secret"/> or an unknown algorithm name throws
/// <see cref="ArgumentException"/>.
/// </summary>
public static string HmacHex(string message, string secret, string? algorithm = null)
{
string name = string.IsNullOrEmpty(algorithm) ? DefaultAlgorithm : algorithm;
if (secret.Length == 0)
throw new ArgumentException("HMAC secret must not be empty.", nameof(secret));
// Both inputs are the exact byte sequences a browser hands to
// crypto.subtle.sign with an HmacImportParams key.
byte[] key = Encoding.UTF8.GetBytes(secret);
byte[] data = Encoding.UTF8.GetBytes(message);
// Each arm fixes the implementation type at compile time. The
// HashData one-shots (key first, then data) allocate no HMAC state
// and dispose nothing.
byte[] tag = name switch
{
Sha1 => HMACSHA1.HashData(key, data),
Sha256 => HMACSHA256.HashData(key, data),
Sha384 => HMACSHA384.HashData(key, data),
Sha512 => HMACSHA512.HashData(key, data),
_ => throw new ArgumentException($"Unsupported HMAC algorithm: {name}", nameof(algorithm)),
};
// ToHexString is uppercase; the reference emits lowercase.
return Convert.ToHexString(tag).ToLowerInvariant();
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →