HMAC (Hash-based Message Authentication Code) is the standard way to sign data with a shared secret. Give it a key, a message, and a hash function, and it produces a signature that can only be computed by someone who knows the key. The recipient can verify the signature — proving both that the message was generated by a party who knew the key, and that the message hasn’t been modified in transit. HMAC is used everywhere: API request signing (AWS, Stripe, Shopify), webhook validation (GitHub, Slack, Twilio), JWT tokens with HS256, and countless custom authentication schemes.
This tool computes HMAC signatures for any message and key using the browser’s Web Crypto API, with support for SHA-1, SHA-256, SHA-384, and SHA-512 as the underlying hash function. Type a key and a message, pick an algorithm, and the hex-encoded signature appears. Everything runs in your browser — the key and message never leave your device.
How HMAC works
HMAC is a specific construction on top of any hash function. Given a key $K$, a message $M$, and a hash function $H$:
Where $K_{\text{ipad}}$ and $K_{\text{opad}}$ are the key XORed with two fixed byte patterns (0x36 and 0x5c, each repeated to fill the hash’s block size), and $|$ is concatenation.
The two-layer structure exists to defeat the length-extension attack that affects naive hash-based signatures. If you just compute $H(K | M)$, an attacker who sees that hash and knows the length of $K$ can compute $H(K | M | X)$ for arbitrary $X$ without knowing $K$, because of how Merkle-Damgård hash functions (SHA-1, SHA-256, SHA-512) construct their internal state. The inner hash in HMAC blocks this: even if an attacker knows the inner hash output, they can’t extend it and then run it through the outer hash without knowing the key.
You don’t need to understand the math to use HMAC — the Web Crypto API takes care of it. But knowing why HMAC exists instead of just SHA-256 saves you from reinventing a broken scheme.
Example: AWS-style request signing
AWS S3’s older V2 signing scheme used HMAC-SHA1. You’d construct a canonical string-to-sign from the request method, headers, and path, then compute HMAC-SHA1 with your AWS secret access key, base64-encode the result, and include it as the Signature parameter. The server would do the same computation server-side — if the two signatures matched, the request was authenticated.
V4 (current) uses HMAC-SHA256 in a more elaborate multi-step derivation to produce a per-request signing key, but the primitive is still HMAC. Most cloud APIs use some variant of this scheme: HMAC a canonical representation of the request with a shared secret to produce a signature that can’t be forged without the secret.
Example: GitHub webhooks
When GitHub sends you a webhook, it includes an X-Hub-Signature-256 header that is sha256= followed by the HMAC-SHA256 of the request body, using a secret you configured on the webhook. You verify by computing HMAC-SHA256(your_secret, request_body) and comparing it (constant-time) to the header value. If they match, the webhook is authentic. If they don’t, either the payload has been tampered with or someone’s trying to forge events.
Example: JWT with HS256
A JSON Web Token with HS256 is structured as base64url(header) + "." + base64url(payload) + "." + signature, where the signature is HMAC-SHA256 of the first two parts joined with a dot, keyed with a shared secret. The receiver validates by recomputing the HMAC over the same concatenation and comparing it to the provided signature. HS256 tokens are cheap to generate and verify but require both parties to share the secret — if you need asymmetric key signing, use RS256 or ES256 instead.
What this tool does not do
It does not support HMAC-MD5 because browser Web Crypto doesn’t — MD5 is deprecated for collision resistance, and shipping it would require bundling a pure-JS MD5 implementation which would add weight for a deprecated algorithm. For new work, use HMAC-SHA256 at a minimum. Legacy systems that still require HMAC-MD5 need a dedicated library.
It does not handle hex-encoded keys automatically — if your API uses hex keys, you’d need to decode them to raw bytes first, which this tool doesn’t offer in the UI. Both key and message are treated as UTF-8 strings. If you need to sign with a binary key, use the underlying computeHmac() function from a script or a more flexible tool.
It does not implement key stretching (PBKDF2, HKDF) — those are separate primitives for deriving a signing key from a password or a master secret. HMAC is the signing step after you already have a proper key. For unauthenticated hashing (no secret key), the hash generator produces the raw digest of the same SHA family.