HMAC Generator & Signature Verifier
Free online HMAC generator & verifier — compute or verify HMAC-SHA256/SHA1/384/512 with Text, Hex or Base64 keys and Hex/Base64/Base64URL output. 100% in your browser; your secret key never leaves the page.
What Is an HMAC?
An HMAC (Hash-based Message Authentication Code) is a short, fixed-length tag that proves a message is both unmodified and authentic — that it was produced by someone who holds a shared secret key. Defined in RFC 2104 and FIPS 198-1, HMAC combines any cryptographic hash function with a secret key in a specific nested construction, written as HMAC(K, m) = H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ m)). The inner hash binds the key to the message; the outer hash wraps the result, which is what makes HMAC resistant to length-extension attacks that affect the raw SHA-1 and SHA-256 functions.
HMAC is everywhere in modern web infrastructure. It signs webhooks so you can confirm an incoming request really came from GitHub, Stripe, Slack, or Twilio and was not forged. It signs API requests (AWS Signature Version 4 is built on HMAC-SHA256) so a server can authenticate the caller without sending a password over the wire. It is the S in HS256: a JWT signed with HS256 carries an HMAC-SHA256 over its header and payload, which you can inspect with the JWT encoder. It also underpins TLS key derivation (HKDF), one-time-password algorithms (HOTP/TOTP), and message integrity in countless internal services.
This tool computes HMAC entirely in your browser using crypto.subtle.sign('HMAC', ...) from the Web Crypto API — the same primitive browsers use during TLS handshakes. Your secret key and message are never uploaded, so it is safe for production signing secrets. Because the same secret can be expressed as raw text, hex, or base64, the tool lets you choose the Key encoding explicitly, and because different providers expect the tag in different forms, you can output Hex, Base64, or Base64URL. The Verify tab lets you check a signature you received, using a constant-time comparison so the check itself does not leak timing information.
const crypto = require('crypto');
// HMAC-SHA256 with a UTF-8 text key, hex output
const hmac = crypto
.createHmac('sha256', 'my-secret-key')
.update('Hello, World!')
.digest('hex');
console.log(hmac);
// → 'cf3141611e22ea26a9cac6fe41d941274dd6653622c83cba13972d177bd69699'
// Verify a signature in constant time
function verify(message, key, expectedHex) {
const actual = crypto.createHmac('sha256', key).update(message).digest();
const expected = Buffer.from(expectedHex, 'hex');
return actual.length === expected.length &&
crypto.timingSafeEqual(actual, expected);
} Key Features
Generate and verify in one tool
Generate a signature in the Generate tab, or paste an Expected HMAC in the Verify tab to authenticate an incoming webhook or token. Verification uses a constant-time comparison so the result never leaks timing information.
Choose the key encoding
Interpret your secret as Text (UTF-8), Hexadecimal, or Base64. This is the setting most other tools omit — and the most common reason two systems compute different HMACs for the same key.
Three output encodings
Output the tag as Hex (GitHub webhooks, AWS), Base64 (Stripe, Twilio, many APIs), or Base64URL (JWTs and URL-safe tokens) so it matches your integration without manual conversion.
Four native algorithms
HMAC-SHA256 by default, plus SHA-1, SHA-384, and SHA-512. All run on the browser's Web Crypto API, so there is no JavaScript crypto library to trust and no performance penalty.
100% client-side and private
Your secret key and message are processed entirely in your browser and never sent to any server. Open the Network tab and you will see zero outgoing requests — safe for production signing secrets.
Live computation
The HMAC recomputes instantly as you edit the message, key, encoding, or algorithm — no Generate button round-trip, so you can experiment with encodings until your value matches the server's.
Built on tested vectors
Output is validated against the official RFC 4231 HMAC test vectors, so you can trust the digests match what OpenSSL, Node's crypto module, and Python's hmac library produce.
HMAC Examples
Quick start — HMAC-SHA256, hex output
Hello, World!
cf3141611e22ea26a9cac6fe41d941274dd6653622c83cba13972d177bd69699
With key "my-secret-key" (Key encoding = Text), algorithm HMAC-SHA256, and Output format = Hex, the message "Hello, World!" produces cf3141611e22ea26a9cac6fe41d941274dd6653622c83cba13972d177bd69699. This is the canonical 64-character hex digest you get from Node's crypto.createHmac('sha256', key).update(msg).digest('hex').
Verify a Stripe-style webhook (Base64 output)
{"id":42,"event":"user.created"} Cd2f7zTKaJFeG6k+t1FcvDPn51OAZ2f4GrxkCUgMhGs=
Many webhook providers send the signature as Base64. With key "whsec_test_secret" (Text), HMAC-SHA256, and Output format = Base64, the JSON body signs to Cd2f7zTKaJFeG6k+t1FcvDPn51OAZ2f4GrxkCUgMhGs=. Paste that value into the Verify tab to confirm the request really came from your provider before processing it. Internally HMAC runs over the same primitive as our SHA-256 hash generator, but keyed with your secret.
RFC 4231 reference vector
what do ya want for nothing?
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
This is test case 2 from RFC 4231, the official HMAC test-vector document. With key "Jefe" (Text), HMAC-SHA256, Hex output, the message "what do ya want for nothing?" yields 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843. Matching this exact value is how you prove an HMAC implementation is correct.
HMAC-SHA512 for a longer digest
The quick brown fox
36f44b125a8a90639dc46733039571792e081e0fd8685ff746784b02ed14aa35629d562c7117cde4a701570551faa5a5e1b7ef1eb5c3bcd4cc1fdb8923fcf14e
Switch the algorithm to HMAC-SHA512 for a 128-character (512-bit) digest. With key "key" (Text) and Hex output, "The quick brown fox" produces the value above. SHA-512 is faster than SHA-256 on most 64-bit hardware and gives a larger output, though SHA-256 remains the interoperability default.
How to Generate & Verify an HMAC
- 1
Choose the algorithm
Pick HMAC-SHA256 (the default and the right choice for nearly all webhooks, APIs, and JWTs), or switch to SHA-1, SHA-384, or SHA-512 to match a system that requires it. All four run natively in your browser via the Web Crypto API.
- 2
Enter the secret key and set its encoding
Type or paste your secret key, then set Key encoding to match how the server interprets it: Text (UTF-8) for a plain string, Hex for a hexadecimal blob, or Base64 for a base64 secret. Getting this wrong is the top cause of HMAC mismatches, so when in doubt try all three.
- 3
Enter the message
Paste the exact bytes you want to sign — for a webhook this is the raw request body, byte-for-byte, with no re-serialization or whitespace changes. The HMAC recomputes live as you edit, with nothing sent to a server.
- 4
Pick the output format and copy
Select Hex (GitHub-style), Base64 (Stripe/AWS-style), or Base64URL (JWT-style) to match what your integration expects, then click Copy to grab the signature.
- 5
Verify an existing signature
Switch to the Verify tab, paste the Expected HMAC from a header or token, and the tool confirms in constant time whether your computed signature matches — so you can authenticate a payload before acting on it.
Common HMAC Mistakes
Key encoding mismatch (the #1 cause of mismatches)
The same secret can be read as raw UTF-8 text, as hex, or as base64 — and each interpretation produces completely different key bytes, so the HMAC will not match if your tool and the server disagree. If a provider gives you a hex or base64 secret, you must decode it to bytes before signing, not sign the string as-is. When a signature fails to verify, try the key under all three Key encoding options first.
// Server stored a base64 secret but you sign the literal string
createHmac('sha256', 'c2VjcmV0LWtleQ==').update(msg) // Decode the base64 secret to raw bytes first
createHmac('sha256', Buffer.from('c2VjcmV0LWtleQ==', 'base64')).update(msg) Output encoding mismatch
An HMAC is raw bytes; hex, base64, and base64url are just different text encodings of the same value. If the server sends a base64 signature and you compare it to your hex digest, they will never match even though the underlying bytes are identical. Match the Output format to what the header or token uses.
// Provider sends base64, you compare hex
expected = 'Cd2f7z...=' // base64
actual = digest('hex') // hex — never matches // Produce the same encoding the provider uses
actual = digest('base64') Signing re-serialized JSON instead of the raw body
Webhook signatures cover the exact bytes the provider sent. If you parse the JSON and re-stringify it, key order, spacing, and number formatting can change, altering the bytes and breaking the signature. Always HMAC the raw request body captured before any parsing.
// Re-serialization changes the bytes body = JSON.stringify(JSON.parse(rawBody)) verify(hmac(body))
// Sign the raw bytes exactly as received verify(hmac(rawBody))
Using == instead of a constant-time compare
Comparing the received signature with == or simple string equality leaks timing information, because the comparison stops at the first differing byte. Over many attempts an attacker can recover a valid tag. Always use a constant-time equality check when verifying.
if (received === computed) { /* trust */ } if (crypto.timingSafeEqual(receivedBuf, computedBuf)) { /* trust */ } What HMAC Is Used For
- Verify webhook signatures
- Providers like GitHub, Stripe, Slack, and Twilio sign each webhook with HMAC over the request body and a secret only you share. Recompute the tag and compare it to the header (for example X-Hub-Signature-256) to confirm the event is genuine before you act on it. Use the Verify tab to do this without writing throwaway code.
- Sign API requests
- Authenticated APIs often require the client to HMAC-sign the request (method, path, timestamp, body) with a shared secret instead of sending the secret itself. AWS Signature Version 4 is the canonical example. This tool lets you reproduce and debug those signatures step by step.
- Guarantee message integrity
- When you pass a token, cookie, or message between services, attaching an HMAC lets the receiver detect any tampering. Because the tag depends on a secret, an attacker cannot recompute it after modifying the data — unlike a plain checksum.
- Understand JWT HS256 signing
- A JWT signed with HS256 is just base64url(header) + '.' + base64url(payload), signed with HMAC-SHA256 and the result emitted as Base64URL. Set the algorithm to SHA-256 and the output to Base64URL here to see exactly how that signature is produced, then cross-check in the JWT encoder.
- Debug a mismatching signature
- When your HMAC will not match the server's, this tool is the fastest way to isolate why: try the key as Text, Hex, and Base64, switch the output between Hex and Base64, and confirm you are signing the exact raw bytes — all without redeploying any code.
How HMAC Works
- RFC 2104 construction
- HMAC is defined as H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ m)), where ipad is the byte 0x36 repeated and opad is 0x5c repeated, both to the hash's block size. A key longer than the block size is first hashed; a shorter key is zero-padded. This two-pass nesting is what gives HMAC its security proof, which holds even if the underlying hash is not collision-resistant.
- Why a keyed hash beats a plain hash
- A plain hash proves only that data was not accidentally corrupted, because anyone can recompute it for any message — including a tampered one. HMAC mixes in a secret, so only key-holders can produce a valid tag. That converts integrity-only into integrity plus authenticity, which is the property webhooks and signed requests actually need.
- Length-extension resistance
- Bare SHA-1, SHA-256, and SHA-512 are Merkle–Damgård hashes vulnerable to length-extension: given H(secret ‖ msg) an attacker can compute H(secret ‖ msg ‖ extra) without knowing the secret. Naive 'secret-prefix MAC' schemes are broken by this. HMAC's outer hash neutralizes the attack, which is the main reason to use HMAC instead of hashing a secret and message together.
- Choosing SHA-256 vs SHA-512
- HMAC-SHA256 produces a 256-bit (64 hex char) tag and is the interoperability default — fast, ubiquitous, and supported everywhere. HMAC-SHA512 produces a 512-bit (128 hex char) tag and is often faster on 64-bit CPUs because SHA-512 uses 64-bit words. Pick SHA-256 unless a specification or peer system requires SHA-512; both are secure for authentication.
- Web Crypto implementation
- This tool calls crypto.subtle.importKey to load your key (decoded from Text, Hex, or Base64) and crypto.subtle.sign('HMAC', ...) to compute the tag, then encodes the raw bytes as Hex, Base64, or Base64URL. It is the same native, audited implementation the browser uses for TLS, running outside the JavaScript engine for speed.
HMAC Best Practices
- Use a key at least as long as the hash output
- For HMAC-SHA256 use a secret of at least 32 random bytes (256 bits); for SHA-512 use 64. A short or low-entropy key is the weakest link. Generate keys from a cryptographically secure random source — never a password or a predictable string.
- Always compare tags in constant time
- Verify signatures with a constant-time comparison (crypto.timingSafeEqual in Node, hmac.compare_digest in Python) rather than == or string equality. A naive comparison returns early on the first differing byte, leaking timing that can let an attacker recover a valid tag byte by byte. This tool's Verify tab already compares in constant time.
- Never log or expose the secret key
- Keep signing secrets out of logs, error messages, URLs, and client-side code. Store them in a secrets manager or environment variables, rotate them periodically, and scope each integration to its own key so one leak does not compromise everything.
- Prefer HMAC-SHA256 or stronger
- Default to HMAC-SHA256; step up to SHA-384 or SHA-512 when a peer requires it. Avoid HMAC-SHA1 for new systems and never use HMAC-MD5. Even though HMAC tolerates a weaker hash better than a raw signature would, starting from a modern hash gives you the most margin.
- Sign the exact bytes, including a timestamp
- Sign the raw, unmodified payload — re-serializing JSON or trimming whitespace changes the bytes and breaks verification. For request signing, include a timestamp or nonce in the signed data and reject stale signatures to prevent replay attacks.
HMAC FAQ
What is HMAC?
How is HMAC different from a plain hash like SHA-256?
How do I verify an incoming webhook signature?
Which key and output encoding does my server use?
Is HMAC-SHA256 secure?
Why no HMAC-MD5 or HMAC-SHA-3 here?
HS256 (HMAC) vs RS256 (RSA) — which should I use for JWTs?
Related Tools
View all tools →Bcrypt Hash Generator & Verifier
Security Tools
Generate and verify bcrypt password hashes online — adjustable cost, $2b$/$2a$/$2y$ prefixes. 100% in your browser; your password is never uploaded.
JWT Decoder
Security Tools
Decode JWT tokens online with our free JWT decoder. Instantly inspect header, payload, signature, expiration, algorithm, and claims. 100% browser-based — your token never leaves your device. No signup, no tracking.
JWT Encoder & Generator
Security Tools
Free online JWT generator & encoder. Build the header and payload, sign with HS256, RS256, or ES256 instantly. 100% in-browser — your secret and key never leave your device.
Free JWT Secret Generator — HS256/384/512
Security Tools
Generate a strong, RFC-correct JWT secret for HS256/384/512 — 100% in your browser, never sent to a server. base64url, base64 or hex; copy for .env.
MD5 Hash Generator & File Checksum Tool
Security Tools
Generate MD5, SHA-256, SHA-1 & SHA-512 hashes online for free. Hash text or files in your browser, verify checksums and copy results. No signup needed.
Random Password Generator — Customizable, Strong & Secure
Security Tools
Generate strong random passwords instantly — free, 100% in your browser. Customize length & characters, batch up to 50 with entropy analysis.