Encryption / Hash

HMAC Calculator

Compute HMAC message authentication codes with SHA-1/SHA-256/SHA-384/SHA-512.

Input text
Secret key
Algorithm:
HMAC Result
Waiting for input...

What is HMAC?

HMAC (Hash-based Message Authentication Code) combines a secret key with a hash function to verify data integrity and authenticity.

Supported Algorithms

  • HMAC-MD5
  • HMAC-SHA1
  • HMAC-SHA256
  • HMAC-SHA384
  • HMAC-SHA512

Use Cases

  • API authentication signatures
  • Message integrity verification
  • Webhook validation

Computes an HMAC (RFC 2104) over UTF-8 text with a shared secret, using SHA-1, SHA-256, SHA-384 or SHA-512, and prints the raw tag as hex. Used to reproduce the signature a server or webhook sender computed, and to debug HS256 tokens and API request signing.

Two hash passes, one block size

RFC 2104 and FIPS 198-1 define the tag as H((K ^ opad) || H((K ^ ipad) || M)), where ipad is the byte 0x36 repeated and opad is 0x5c. Before hashing, the key is normalized to a single hash block: 64 bytes for SHA-1 and SHA-256, 128 bytes for SHA-384 and SHA-512, zero-padded when shorter and hashed down when longer. Two consequences show up in the result box. The tag is always exactly one digest long — 20, 32, 48 or 64 bytes — because the outer pass hashes a fixed-size block pair. And a key longer than the block size stops adding entropy: with SHA-256 selected, a 200-byte secret and its SHA-256 digest give identical tags.

The key field holds characters, not hex bytes

TextEncoder converts both fields to UTF-8 before crypto.subtle.sign, so the key is literally the characters you typed. A 64-character hex string is used as 64 ASCII bytes, not as the 32 raw bytes it may denote elsewhere; a Chinese passphrase costs 3 bytes per character. There is no key-format switch, no Base64 key option and no MD5. The empty-input guard calls trim(), yet the signature covers the untrimmed string, so a trailing newline in the textarea changes every byte of the tag while the output still looks perfectly valid. The Uppercase checkbox only reformats hex digits after signing.

Matching a signer byte for byte

Feed the tool the exact bytes that were signed. For HS256 the signing input is base64url(header), a literal dot, then base64url(payload), all plain ASCII; re-encoded to hex it must equal the third JWT segment. GitHub prefixes the same hex value with sha256= in X-Hub-Signature-256 and computes it over the raw request body, so pretty-printing JSON changes whitespace and key order and therefore the tag. Expect a fixed length per algorithm: 40 characters for SHA-1, 64 for SHA-256, 96 for SHA-384, 128 for SHA-512. A mismatch in length means the signer used a different algorithm than the one selected.

Worked example

The built-in example, HMAC-SHA256

Input
message: Hello, DevToolbox! | key: my-secret-key | algorithm: SHA-256
Output
953537baf822bdc213ea5076e123c542fb87986940e35c9b238f8247088a3538

The message is 18 UTF-8 bytes and the key 13 bytes, well under the 64-byte SHA-256 block, so the key is zero-padded and used directly in both passes. The tag is 32 bytes, printed as 64 lowercase hex characters; ticking Uppercase returns the same value as 953537BA...3538. Appending one newline to the message yields ef961f26...bf3605 instead.

Limits and pitfalls

  • Both inputs are UTF-8 text: a secret issued as raw or hex bytes cannot be reproduced unless you retype it as the exact characters a signer hashes.
  • Only SHA-1, SHA-256, SHA-384 and SHA-512 are available through Web Crypto, so HMAC-MD5 tags from legacy systems cannot be checked.
  • An empty or whitespace-only key is refused even though RFC 2104 allows a zero-length key, so such tags must be computed elsewhere.
  • The tool needs a secure context (HTTPS or localhost); on plain HTTP crypto.subtle is undefined and no tag is produced.
  • Output is hex only, recomputed 300 ms after the last keystroke: there is no Base64 tag, no truncation length and no comparison against an expected value.

Related tools

First decide whether you are computing a tag or judging one

Your situationThis HMAC pageThe better fit
A signature arrived and the answer is yes or no, not a fresh valueThe result box holds one string with click-to-copy and nothing else: there is no field for the value the sender supplied and no comparison anywhere on the page, so 64 hex characters get matched by eye.Compare in code — crypto.timingSafeEqual(Buffer.from(mine, "hex"), Buffer.from(theirs, "hex")) in Node, which throws outright when the two buffers differ in length. The JWT decoder on this site will not do it: it states that it parses tokens and never verifies the signature.
The receiver wants the tag in Base64Hex is the only output shape (optionally uppercased), so a Base64 endpoint means turning those 64 characters back into 32 bytes somewhere else.openssl dgst -sha256 -hmac "$KEY" -binary | base64 emits the raw tag straight into Base64; with the secret in a shell variable it is one line instead of a conversion.
The secret was rotated last night and 200 stored deliveries need re-checkingOne body, one key, one tag at a time. The key field survives when you swap the message, but there is no queue, no file picker and no export, so every body is a paste and every tag a click.Loop in the shell: for f in deliveries/*.json; do openssl dgst -sha256 -hmac "$SECRET" "$f"; done. dgst reads the file bytes directly, so a trailing newline in the log is not silently dropped the way $(cat ...) drops it.
You are rebuilding the 6-digit code an authenticator app showsHMAC-SHA1 stops at the full 20-byte tag, printed as 40 characters. The RFC 4226 step — offset from the low nibble of the last byte, 31-bit mask, mod 10^6 — is yours to do by hand.oathtool --totp -b <BASE32SECRET> performs the time step, the truncation and the zero-padding together. This page has no truncation and takes no Base32 secret.
The shared secret is a password somebody typedCharacters typed into the key field are the key, with no stretching at all: an eight-letter password is 8 bytes, and each guess costs exactly one HMAC.Stretch it first in the PBKDF2 tool here — hex salt of your choosing, default 210000 iterations, accepted range 1000 to 2000000 — but note the derived bytes cannot be typed into this key field as bytes, so sign where you derive them.
The payload itself must not be readable on the wireAuthenticity only: the body travels in the clear beside its tag, so whoever intercepts the request reads the JSON and could even rewrite it.AES-GCM tool on this site (passphrase-derived key, 12-byte IV, 16-byte tag) both conceals and authenticates. Keep HMAC for bodies that are already public and need tamper evidence alone.

One concrete scenario

A 64-character field picks the algorithm for you

Juggling two partners in one afternoon is where the dropdown bites. It keeps whatever you chose last: after signing SHA-512 for the partner that wants 128-character tags, you load the next body and press Example to check the layout. It fills Hello, DevToolbox! and my-secret-key but leaves SHA-512 selected, so 128 characters come back again — twice the width of the 64-character field the new partner sent, and nothing on the page mentions it. Set the dropdown to SHA-256 before pasting the real 640-byte body, and leave Uppercase off when their field stores lowercase, since the tag would be identical and still fail a string comparison. If the incoming width matches none of the four, the sender truncates and this page cannot follow.

Frequently Asked Questions

How is HMAC different from a plain hash?
Anyone can compute a plain hash; HMAC is keyed — only holders of the secret can produce or verify it. That is why API request signing and webhook verification (GitHub, Stripe) use HMAC: the receiver recomputes it with the shared key to confirm both origin and integrity.
Why must HMAC comparison use constant time?
A normal === comparison returns at the first mismatched character, letting attackers deduce the correct signature byte by byte by measuring response times (timing attack). Use crypto.timingSafeEqual or your language’s constant-time comparison in real code.
Can HMAC replace password hashing?
No. HMAC is fast, which makes GPU brute force cheap. Password storage needs deliberately slow hashes (bcrypt, Argon2id, PBKDF2). HMAC’s job is message authentication, not credential storage.

Content reviewed: