HMAC SHA256 Generator

Compute HMAC-SHA256, SHA-512, SHA-1 or MD5 with text, hex or Base64 keys, and check a GitHub, Stripe, Slack or LINE webhook signature with the likely cause of a mismatch.

  • Runs in your browser
  • Your data never leaves your browser
  • Free · No Sign-Up
Custom HMAC takes any key and message and lets you choose the algorithm. Each other format builds the signed string the way that platform documents it, sets the algorithm, and adds a Value to send row with the header or parameter the platform uses. Feishu, DingTalk and NAVER Cloud sign a request that you send, so they have no body box. Example loads a published test case and is shown for the formats that have one.
bits Keeps only the leftmost bits of the HMAC (RFC 2104 §5), in all three encodings. Enter a multiple of 8, from 8 up to the full length; leave it empty for the full value. A note appears below half of the output or below 80 bits. Available in Custom HMAC only.

The bytes to sign. In Custom HMAC they can be text (encoded as UTF-8), hex or Base64; the other formats take the raw request body as text. The box stores every line break as LF, so choose CRLF if the sender used CRLF. Hex may contain spaces, colons and a leading 0x. Base64 may use the standard or the URL-safe alphabet, with or without padding. Full-width characters from an IME are read as ASCII in hex and Base64. Text in Shift_JIS, GBK or EUC-KR has to be pasted as hex.
In Custom HMAC the key can be text (UTF-8), hex or Base64. The other formats read it as the platform does: most as text (for Stripe including the whsec_ prefix), Chatwork after Base64 decoding, and Standard Webhooks by Base64-decoding the part after whsec_. HMAC first hashes a key that is longer than the hash block (64 bytes; 128 for SHA-384 and SHA-512) and pads a shorter key with zeros (RFC 2104). A note appears for a key longer than the block or shorter than the output. Spaces at either end of a text key are part of the key.
Result Updates as you type. Hex, Base64 and Base64url are the same bytes in three encodings. Value to send, added by the platform formats, is the header or parameter as that platform writes it. Signed string shows exactly what went into the HMAC, with line breaks and tabs made visible. Ctrl+L (⌘+L on a Mac) empties the text fields while the focus is in the tool.

Enter a key or a message and the HMAC appears here.

Paste what you received: a header such as sha256=…, t=…,v1=… or v1,…, a DingTalk URL with sign=…, or a bare hex or Base64 value. Bytes are compared, so hex in either case and Base64 both work; a truncated value of at least 10 bytes matches as a prefix. When it does not match, the tool tries the usual causes one at a time (a final line break, CRLF, spaces at the ends, reformatted JSON, another key encoding, key and message swapped, another algorithm) and names the first one that produces the pasted value. On your server, compare in constant time.

The key, message and signature are processed in this tab with the Web Crypto API. Nothing is uploaded or saved; reloading the page clears them.

Read the full guide HMAC Explained: How HMAC-SHA256 Signs Webhooks and API Requests
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.

The result is HMAC as defined in RFC 2104: a key longer than the hash block (64 bytes for SHA-256, 128 for SHA-512) is hashed first, and a shorter key is padded with zeros. The tool tells you when either happens, and when a key is shorter than the output, which RFC 2104 §3 discourages. Truncate to keeps the leftmost bits, as in RFC 2104 §5 and the RFC 4231 test case 5.

Examples You Can Reproduce

GitHub’s published test values. GitHub’s guide to validating webhook deliveries gives the secret It's a Secret to Everybody and the payload Hello, World!. Choose GitHub webhook (or press Example) and the tool shows:

X-Hub-Signature-256: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17

That is the value in GitHub’s documentation. The same check works with Slack’s example (signing secret 8f742231b10e8888abcd99yyyzzz85a5, timestamp 1531420618, result v0=a2114d57…) and LINE’s example; both load with Example in their formats.

A body with one extra line break. Copy the payload from a terminal and the line often ends with a newline. With Hello, World! plus a line break, the same secret gives:

8fde2e970f9163923fb1cb61bb945626ff2b4091d87e622ee3ad600160592325

Paste GitHub’s sha256=757107ea… header into Signature to check and the tool answers No match. Likely cause: it matches without the final line break. The sender signed the body without it. The note under the result also says that the body ends with a line break. Other causes the checker tries: CRLF instead of LF, spaces around the key, JSON that was minified or indented after it arrived, a hex or Base64 key read as text, key and message swapped, a Stripe timestamp that differs from t= in the header, and another algorithm (GitHub’s legacy X-Hub-Signature is HMAC-SHA1).

RFC 4231 test vectors with a hex key. Test case 1 uses twenty 0x0b bytes as the key and Hi There as data. Set the key encoding to Hex and paste 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b:

b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7

This equals the HMAC-SHA-256 line in RFC 4231 §4.2. Left as text, the same 40 characters are 40 ASCII bytes and the result is 0dff03ee…, which is why the encoding selector matters. Test case 5 (0c × 20, Test With Truncation) truncated to 128 bits gives a3b6167473100ee06e0c796c2955552b. The tests for this page check every RFC 2104, RFC 2202 and RFC 4231 vector, plus random inputs against Node.js crypto.createHmac and Python hmac.

Signing Formats

FormatSigned stringKeySent as
GitHubraw bodysecret as textsha256= + hex
Stripet + . + bodywhole whsec_… stringt=…,v1=… (hex)
Slackv0: + timestamp + : + bodysigning secretv0= + hex
Standard Webhooksid . timestamp . bodyBase64 after whsec_v1, + Base64
Shopifyraw bodyclient secretBase64
TwilioURL + sorted POST parametersauth tokenBase64 of HMAC-SHA1

Stripe and Slack expect you to reject timestamps more than 5 minutes old, and the tool warns when the timestamp is outside that window. For Twilio form bodies, parameters are percent-decoded, sorted by name and appended as name + value; for a JSON body only the URL is signed and the tool checks its bodySHA256 against the body.

How It Differs From Other HMAC Pages

Tested on 2026-10-01 with GitHub’s example. CodeShack and devglan both give the correct 757107ea…. CodeShack computes in the browser, takes the key only as text in a single-line field, and has no verifier. devglan sends the message and the secret key to its server in a POST request, both to generate and to verify, and when the message is empty it keeps showing the previous result. Its verifier accepts a bare value; sha256=… as copied from the header is rejected with “MAC must be hex or Base64 encoded”. This tool computes everything in the tab, accepts hex and Base64 keys with the position of any invalid character, and explains a mismatch instead of only reporting it.

Limits

  • Algorithms: MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512. SHA-3, RIPEMD-160 and SM3 are not included; the Web Crypto API has none of them.
  • A text box turns every line break into LF. If the sender used CRLF, choose CRLF. For other exact bytes, paste the message as hex or Base64.
  • Text is encoded as UTF-8. A message stored as Shift_JIS, GBK or EUC-KR must be pasted as hex.
  • The checker runs in your browser. On your server, compare in constant time, and use the raw body bytes before any JSON parsing; the JWT Generator covers HS256 tokens, which are HMAC-SHA256 too.
  • Feishu, DingTalk and NAVER Cloud formats sign a request you send, so there is no body field for them.

For a plain digest without a key, use the Hash Generator; for one-time codes built on HMAC-SHA1, use the TOTP Generator.

FAQ

Is my secret key sent anywhere or saved?

No. SHA-1, SHA-256, SHA-384 and SHA-512 run on the browser's Web Crypto API, and MD5 and SHA-224 run on code inside the page. The tool makes no network requests and does not write the key or message to localStorage, cookies or the URL. This page also loads no analytics or ad scripts. Reloading the page clears every field.

Why does my webhook signature not match?

In most cases the bytes you hash are not the bytes the sender signed: the body was parsed and serialized again, a final line break was added or removed, line breaks changed between LF and CRLF, or the timestamp or prefix is missing from the signed string. Paste the signature header into Signature to check. If it does not match, the tool tries these variants and tells you which one produces the pasted value.

Which algorithm should I use?

Use the one the other side expects. For a new design, HMAC-SHA256 is the common choice: GitHub, Stripe, Slack, Shopify, LINE and DingTalk use it. HMAC-SHA1 is still used by Twilio and by GitHub's legacy X-Hub-Signature header. HMAC-MD5 is included for old systems and for the RFC 2104 test vectors.

Should the key be text, hex or Base64?

Follow the platform's documentation. GitHub, Stripe, Slack and LINE use the secret string as text, including any prefix such as whsec_. Chatwork and Standard Webhooks (Svix) Base64-decode the secret first. RFC test vectors list keys in hex. If the tool finds that your pasted signature matches another key encoding, it says so.

Is it safe to compare signatures with ==?

Not in server code. Use a constant-time comparison: crypto.timingSafeEqual in Node.js (it throws when the lengths differ, so check the length first), hmac.compare_digest in Python, or hmac.Equal in Go. GitHub's documentation gives the same advice. The comparison in this tool runs only in your browser, so timing does not matter here.