How to use
- Type or paste the exact text you need to authenticate into the Message box. Trailing newlines and CRLF line endings are part of the bytes, so match whatever the other side signs, character for character.
- Enter your shared secret in Secret key.
- Set Key encoding to Text (UTF-8) for a string secret like
whsec_…, or to Hex bytes if the key was given to you as hex digits that should decode to raw bytes. - Choose the Algorithm. HMAC-SHA-256 is selected by default; switch to SHA-1, SHA-384 or SHA-512 only to match the system you are talking to.
- The tag appears live in both hex and Base64 as you type — copy whichever format the receiver expects with the button above each field.
How it works
Take the message ledger|acct=8842|amount=305.00, the key wombat-secret-42, and HMAC-SHA-256. Both strings become their UTF-8 bytes, the key is imported into crypto.subtle as a raw HMAC key, and the browser runs the two-pass construction: it hashes the key XORed with an inner pad concatenated with the message, then hashes the key XORed with an outer pad concatenated with that first digest. The 32-byte result is:
hex 5b8ab63d0065ac1d949d3d7b3fed67e6805b30986363c774417c6124edabae48
base64 W4q2PQBlrB2UnT17P+1n5oBbMJhjY8d0QXxhJO2rrkg=
Both outputs are the same 256 bits — hex spends two characters per byte, Base64 packs three bytes into four characters, which is why the strings look nothing alike. Change one character of the message or key and about half of the tag’s bits invert — there is no partial credit: a tag either matches to the bit or it does not. SHA-256 works on 64-byte blocks, so a key shorter than that is zero-padded and a key longer than 64 bytes is hashed down to 32 first — extra key length past the block size buys you nothing.
Use cases & limitations
The everyday reason to compute an HMAC is verifying an inbound webhook. Stripe, GitHub and most providers sign each payload with a shared secret and send the tag in a header; you recompute it over the raw body and compare. The same construction signs outbound API requests, stamps tamper-evident session cookies, and protects expiring download URLs. HS256-signed JWTs are HMAC-SHA-256 under the hood — if you are working with those, pair this with the JWT decoder to read the header and claims. When you only need to fingerprint data for integrity and there is no secret involved, a keyless digest from the hash generator is the right tool instead.
Two honest limits. HMAC is symmetric: every party that can verify a tag can also forge one, so it proves a message came from someone holding the key — not which specific person — and gives you no non-repudiation the way an asymmetric signature would. And comparing tags safely is its own problem; a production verifier should use a constant-time comparison, not the == your eye does here, to avoid leaking timing. Treat this page as the place to compute and eyeball a tag, not as the verification path in a live service.
Privacy note
Signing runs entirely in your browser through crypto.subtle, and the page makes no network requests — you can watch the network tab while you type and see nothing leave. Your message and key are never stored or transmitted. That said, a secret key is exactly the sort of thing worth guarding: prefer a test-mode secret, or mint a throwaway one with the key generator rather than pasting a live production credential into any website, including this one.