Skip to content

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.

No Tracking Runs in Browser Free
100% in your browser — your message and secret key never leave your device.
Generated HMAC

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. 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. 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. 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. 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. 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.

✗ Wrong
// Server stored a base64 secret but you sign the literal string
createHmac('sha256', 'c2VjcmV0LWtleQ==').update(msg)
✓ Correct
// 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.

✗ Wrong
// Provider sends base64, you compare hex
expected = 'Cd2f7z...=' // base64
actual   = digest('hex') // hex — never matches
✓ Correct
// 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.

✗ Wrong
// Re-serialization changes the bytes
body = JSON.stringify(JSON.parse(rawBody))
verify(hmac(body))
✓ Correct
// 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.

✗ Wrong
if (received === computed) { /* trust */ }
✓ Correct
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?
HMAC (Hash-based Message Authentication Code) is a way to prove both the integrity and the authenticity of a message using a shared secret key. You feed a message and a secret key into a hash function (here SHA-1, SHA-256, SHA-384, or SHA-512) in the specific nested construction defined by RFC 2104, and you get a fixed-length tag. Anyone who knows the secret can recompute the tag and confirm the message was not altered and came from someone who holds the key. It is the standard mechanism behind webhook signatures, signed API requests, and the HS256 family of JWT tokens.
How is HMAC different from a plain hash like SHA-256?
A plain hash such as SHA-256 only proves integrity: anyone can recompute it, so anyone can also forge a matching hash for a tampered message. HMAC mixes in a secret key, so only parties holding that key can produce or verify a valid tag — that adds authenticity on top of integrity. HMAC also uses a nested two-pass construction (inner and outer hashing with key-derived pads) that makes it immune to the length-extension attacks that affect raw SHA-1 and SHA-256. In short: use a hash to detect accidental corruption, use HMAC to detect deliberate tampering by an attacker who does not know your key.
How do I verify an incoming webhook signature?
Take the raw request body exactly as received (do not re-serialize the JSON — even reordered keys break the signature), select the same algorithm your provider uses (usually HMAC-SHA256), enter your signing secret with the correct Key encoding, and set the Output format to match the header (Hex for GitHub's sha256= prefix, Base64 for Stripe/Twilio-style headers). Then open the Verify tab, paste the signature from the header (such as X-Hub-Signature-256), and the tool reports match or mismatch using a constant-time comparison. Only trust the payload if it matches.
Which key and output encoding does my server use?
There is no universal answer — it depends on your provider, which is exactly why this tool exposes both as explicit choices. Common patterns: GitHub uses a UTF-8 text secret and lowercase hex output with a sha256= prefix; Stripe uses a text secret and base64; AWS Signature V4 uses derived binary keys and hex; many internal systems hand out base64 or hex secrets that must be decoded to raw bytes before signing. If your HMAC does not match, the encoding is almost always the culprit — try the same key under Text, Hex, and Base64 to find the interpretation the server expects.
Is HMAC-SHA256 secure?
Yes. HMAC-SHA256 is widely considered secure and is the recommended default for message authentication. Its security comes from the HMAC construction plus a strong underlying hash, and it remains safe even though collision attacks exist against the bare hash — HMAC does not rely on collision resistance the way a digital signature does. The real-world risks are operational, not algorithmic: a weak or leaked secret key, logging the key, or using a non-constant-time comparison. Use a long random key and compare tags in constant time and HMAC-SHA256 will hold up.
Why no HMAC-MD5 or HMAC-SHA-3 here?
By design. This tool exposes only SHA-1, SHA-256, SHA-384, and SHA-512 because those are the hash functions the browser's native Web Crypto API supports, which keeps everything fast and entirely client-side with no extra libraries. HMAC-MD5 is omitted because MD5 is obsolete and you should not start new systems with it. HMAC-SHA-3 is omitted because Web Crypto does not implement SHA-3; adding it would require shipping a JavaScript polyfill. For virtually all modern use cases HMAC-SHA256 is the correct choice anyway.
HS256 (HMAC) vs RS256 (RSA) — which should I use for JWTs?
HS256 signs and verifies a JWT with a single shared secret using HMAC-SHA256, while RS256 signs with an RSA private key and verifies with the matching public key. Use HS256 when one party both issues and validates the tokens (a monolith, or trusted internal services that can safely share the secret) — it is simpler and faster. Use RS256 when third parties or many services must verify tokens but must not be able to mint them, since you can distribute only the public key. You can explore the encoded structure of both in the JWT encoder; the HS256 signature is exactly an HMAC-SHA256 over the header and payload.

Related Tools

View all tools →