CruxDevCruxDev Tools Prompts
security Utility • Zero-Server Privacy • 100% Client-Side

HMAC Generator

Generate HMAC signatures with SHA-256 or SHA-512 using a secret key — runs fully offline.

Overview

Computes an HMAC over your message with your own secret key using the browser's native Web Crypto implementation, offering HMAC-SHA-256, HMAC-SHA-512 and HMAC-SHA-1, and printing the digest as lowercase hex. The result recomputes on every keystroke, and neither the message nor the key is ever sent anywhere — the key is imported as non-extractable so even the page's own scripts cannot read it back out.

How It Works

Type the message, pick the hash, and enter the secret key. All three inputs are live: each change re-imports the key and re-signs, so the hex digest below always matches what is currently on screen. If Web Crypto rejects the input — an empty key, or a hash your browser does not expose — the digest area is replaced by the exact error message the platform threw, rather than a stale or silently wrong value.

Step-by-Step Usage Guide

  1. Paste the exact bytes your server signs — for webhook verification that is usually the raw request body, before any JSON parsing or re-serialisation reorders keys or changes whitespace.
  2. Choose the algorithm your provider documents. SHA-256 is the modern default; SHA-1 is present only so you can verify or debug legacy systems that still use it.
  3. Enter the secret key as your server holds it in plain text. If the server base64- or hex-decodes the secret first, decode it yourself and use the resulting characters, or the digests will not agree.
  4. Compare the hex output against the signature header you received. A mismatch with the right key almost always means the message bytes differ, not the algorithm.

Technical Specifications & Standards

HMAC (RFC 2104) is not a plain hash of the key concatenated with the message; it is H((K xor opad) || H((K xor ipad) || M)), a two-pass construction that resists the length-extension attacks which break naive keyed Merkle-Damgard hashes. This tool does not implement any of that by hand: it calls crypto.subtle.importKey('raw', ...) with { name: 'HMAC', hash } and then crypto.subtle.sign, so the primitive is the browser's own audited implementation. The signature comes back as an ArrayBuffer and is rendered by mapping each byte through toString(16) with padStart(2, '0'), giving the lowercase, unpadded hex most server libraries emit by default. One detail decides whether your digest matches your backend: the key here is the UTF-8 bytes of the text you typed. Servers that store a secret as hex or base64 and decode it to bytes before signing are using different key material, and will produce a different digest for the same visible string.

Targeted Use Cases

  • Verifying an incoming webhook signature by hand when a Stripe, GitHub or Shopify delivery is being rejected and you need to see whether the fault is the secret, the algorithm or the body bytes.
  • Signing API requests for services that require an HMAC of a canonical string, and checking your client's output byte for byte before shipping it.
  • Confirming that two implementations in different languages agree, by feeding both the same message and key and diffing the hex.
  • Teaching or reviewing why HMAC is used instead of a bare hash, with a live digest that changes as the key or message changes.

Notes & Gotchas

  • Treat HMAC-SHA-1 as verification-only. It is offered for compatibility with older systems; do not choose it for anything new.
  • Compare signatures on your server with a constant-time function such as crypto.timingSafeEqual. A plain === leaks timing information that can be used to forge a signature byte by byte.
  • Remember that the key is read as UTF-8 text. Hex- or base64-encoded secrets must be decoded to their real bytes first, which is the single most common reason a correct-looking digest does not match.
  • Even though nothing leaves your browser, avoid pasting live production secrets into any web page. Use a staging key when you only need to prove the mechanism works.

Frequently Asked Questions

Is my secret key uploaded anywhere?

No. The key is imported directly into the browser's Web Crypto engine and marked non-extractable, and the signing happens in the page. There is no network request involved, so the key never leaves your machine.

Why does my digest differ from my server's?

Usually the key encoding or the message bytes. This tool signs the UTF-8 bytes of the key you typed; if your server decodes a hex or base64 secret into raw bytes first, the key material differs. The other frequent cause is signing a re-serialised JSON body instead of the exact raw bytes that were received.

Why are only SHA-256, SHA-512 and SHA-1 available?

Those are the HMAC hashes the Web Crypto API exposes for signing across browsers. SHA-384 and the SHA-3 family are not uniformly available, so offering them would produce errors on some browsers rather than results.

Can I use this to verify a JWT signature?

For HS256 tokens, yes, in principle: the signing input is the base64url header and payload joined by a dot, and the HMAC of that string with the secret is the signature — though the signature in a JWT is base64url, not hex, so you would need to convert before comparing.