Skip to content
AgileLogics

HMAC Generator

Computes a keyed-hash message authentication code (HMAC) for a message, using SHA-1, SHA-256, SHA-384, or SHA-512 as the underlying hash, via the browser's native Web Crypto API.

How it works

Enter the message, its secret key in the second field, choose a hash algorithm, and click Generate.

What does an HMAC generator do?

An HMAC generator combines a message, a secret key and a hash function into a short tag called a message authentication code. Anyone holding the same key can recompute the tag and confirm the message wasn't altered. Without the key, producing a valid tag is infeasible. This tool computes HMAC with SHA-1, SHA-256, SHA-384 or SHA-512.

HMAC is specified in RFC 2104. The construction hashes twice: first the key mixed with an inner pad plus the message, then the key mixed with an outer pad plus that first result. Keys shorter than the hash's block size are padded with zeros, and longer keys are hashed down first. That nesting is what makes HMAC safe where a naive sha256(key + message) is not, because SHA-256 on its own allows length-extension attacks.

How to compute an HMAC here

  1. Paste the exact message into the Message box.
  2. Type or paste the key into the Secret key box.
  3. Choose the hash algorithm. SHA-256 is selected by default.
  4. Click Generate and copy the lowercase hex tag.

Both fields are encoded as UTF-8 and nothing is trimmed, so a space at the end of the key produces a different tag. With the message The quick brown fox jumps over the lazy dog, the key key gives f7bc83f4...2d1a3cd8 under SHA-256, while key (with a trailing space) gives c5c9b038...fe75aa97.

The computation goes through SubtleCrypto.sign(), the browser's native implementation, which is why only the four hashes Web Crypto supports are offered. There's no HMAC-MD5, HMAC-SHA224 or HMAC-SHA3 option.

Verified against the RFC test vectors

RFC 4231 publishes HMAC-SHA-2 test cases, and its test case 2 is one you can type: the key is Jefe and the data is what do ya want for nothing?. The same inputs appear in RFC 2202 for HMAC-SHA1. This tool returns:

SHA-1:   effcdf6ae5eb2fa2d27416d5f184df9c259a7c79
SHA-256: 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
SHA-384: af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649
SHA-512: 164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737

All four match the published values. You can reproduce the SHA-256 line with OpenSSL:

printf '%s' 'what do ya want for nothing?' | openssl dgst -sha256 -hmac Jefe
Algorithm Block size Tag length Hex characters
HMAC-SHA1 64 bytes 160 bits 40
HMAC-SHA256 64 bytes 256 bits 64
HMAC-SHA384 128 bytes 384 bits 96
HMAC-SHA512 128 bytes 512 bits 128

Where you'll meet HMAC

Webhooks. GitHub signs each delivery with HMAC-SHA256 of the raw payload and sends it in the X-Hub-Signature-256 header, prefixed with sha256=. Its webhook validation guide also tells you to compare signatures with a constant-time function rather than ==, so response timing doesn't leak how many characters matched.

Signed API requests. Many APIs have the client HMAC a canonical string (method, path, timestamp, body hash) and send the tag in a header.

JSON Web Tokens. The HS256, HS384 and HS512 algorithms in RFC 7518 are HMAC with SHA-256, SHA-384 and SHA-512. JWTs carry the tag in Base64url, not hex, so the output here won't look identical even when the bytes agree.

When the tags don't match

Symptom Likely cause
Every tag differs from the server's Wrong algorithm, or the key has a stray space or newline
Matches for some requests, not others You're hashing parsed-and-reprinted JSON instead of the raw body
Server value is 64 hex chars plus sha256= Strip the prefix before comparing
Server value ends in = or uses - and _ It's Base64 or Base64url, not hex
Key looks like hex but still fails The service decodes the key to bytes; this tool uses it as text

Choosing a key

The key is the whole security of an HMAC, so it has to be secret and unpredictable. RFC 2104 discourages keys shorter than the hash output (32 bytes for SHA-256). A long random string from the password generator works as a text key, or use the random hex generator if both sides will treat the hex string itself as the key.

An HMAC is a keyed checksum. It doesn't hide the message, and it isn't a password-hashing scheme. For an unkeyed fingerprint of text, use the SHA-256 hash generator. For a MAC built on SHA-3 rather than SHA-2, look at KMAC, covered on the SHA3-256 generator page.

Last updated

Frequently asked questions

Is my secret key sent to your server?
No. The message and key are passed to your browser's built-in Web Crypto API (crypto.subtle.importKey and crypto.subtle.sign), and the tag is computed on your device. Nothing is uploaded or stored.
Can I enter the key as hex or Base64?
No. The key field is treated as plain text and encoded as UTF-8, so typing 0b0b0b uses the six characters 0, b, 0, b, 0, b as the key, not three bytes. If the system you're matching stores its key as raw bytes, compute the HMAC in code with the decoded key instead.
Why doesn't my HMAC match the webhook signature?
The bytes differ somewhere. The usual culprits are signing a re-serialized or pretty-printed JSON body instead of the raw request body, a trailing newline, a header prefix such as sha256= left on the expected value, the wrong algorithm, or a key with stray whitespace.
Is HMAC-SHA1 still safe?
The known SHA-1 collision attacks don't carry over to HMAC-SHA1, as RFC 6194 explains, so existing HMAC-SHA1 integrations aren't broken by them. For anything new, pick HMAC-SHA256, which is the default here.
Why is the output empty when I leave the message blank?
The tool skips empty messages instead of computing an HMAC over zero bytes. Leaving the key blank shows the error 'Enter a secret key in the second field.'