---
url: https://findutils.com/guides/hmac-generator-guide
title: "HMAC Generator: Create and Check HMAC-SHA256 Signatures"
description: "Generate HMAC-SHA1, SHA-256, SHA-384 or SHA-512 signatures with the free FindUtils HMAC Generator and check GitHub, Stripe and Slack webhook signatures."
category: security
content_type: guide
guide_type: subtopic
cluster: security
pillar_slug: complete-guide-to-online-security-tools
subtopic_order: 5
locale: en
read_time: 9
status: published
author: "olgunozoktas"
published_at: 2026-10-19T08:08:00Z
excerpt: "Type a message and a secret key, pick SHA-256, and get the hex HMAC that webhook providers put in their signature headers. This guide shows a test vector you can reproduce, how GitHub, Stripe and Slack build the signed message, and the six reasons a signature does not match."
tag_ids: ["security", "hmac", "webhooks", "api-development", "signatures"]
tags: ["Security", "HMAC", "Webhooks", "API Development", "Signatures"]
primary_keyword: "hmac generator"
secondary_keywords: ["hmac sha256 generator", "generate hmac online", "verify webhook signature", "hmac signature mismatch", "hmac vs hash", "hmac key length"]
tool_tag: "hmac-generator"
related_tool: "hmac-generator"
related_tools: ["hmac-generator", "random-key-generator", "md5-hash-generator", "jwt-decoder", "unix-timestamp", "diff-checker", "two-fa-code-tester"]
updated_at: "2026-10-19T08:08:00Z"
og_image: "/images/content/guides/hmac-generator-guide-cover-20261019.webp"
image_alt: "A message card and a brass key feed along dotted lines into a funnel that points to a green seal with a check mark, above a row of small colored tiles."
---

An HMAC generator takes a message and a secret key and returns a short fingerprint that only someone with the same key can produce. Paste the message into the FindUtils [HMAC Generator](/security/hmac-generator/), type the secret key, choose HMAC-SHA256, and the signature appears at once as lowercase hexadecimal, ready to compare with the value a webhook or API sent you. It is free, needs no signup, and on the tool page the HMAC is computed in your browser.

This guide covers what HMAC is, how to use each field, a test vector you can reproduce in ten seconds, how GitHub, Stripe and Slack build the message they sign, and why a signature you compute by hand often does not match.

## What Is an HMAC?

HMAC (hash-based message authentication code) is defined in [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104). It runs a hash function twice, mixing the key into both passes:

```text
HMAC(K, text) = H(K XOR opad, H(K XOR ipad, text))
```

`ipad` and `opad` are two fixed padding strings (the bytes `0x36` and `0x5C` repeated to the hash's block size). The result proves two things at once: the message was not changed, and it was signed by someone who knows the key. A plain hash proves only the first, because anyone can recompute `SHA-256(message)` after editing the message. Without the key, nobody can compute a matching HMAC.

The tool offers four algorithms. Each output is the hash length written as hex, two characters per byte, and the tool shows that length under the result:

| Algorithm | Output | Hex length | Hash block size |
|-----------|--------|------------|-----------------|
| HMAC-SHA1 | 160 bits | 40 characters | 64 bytes |
| HMAC-SHA256 | 256 bits | 64 characters | 64 bytes |
| HMAC-SHA384 | 384 bits | 96 characters | 128 bytes |
| HMAC-SHA512 | 512 bits | 128 characters | 128 bytes |

The block sizes come from RFC 2104 (64 bytes for SHA-1 and the hashes of its era) and [RFC 4231](https://www.rfc-editor.org/rfc/rfc4231), which gives 128 bytes for SHA-384 and SHA-512. They matter for one rule: a key longer than the block size is hashed first, so making it longer still adds nothing.

## How to Generate an HMAC Online

### Step 1: Paste the Message

Open the [HMAC Generator](/security/hmac-generator/) and paste the exact text that was signed into Message. For a webhook, that is the raw request body, byte for byte, plus whatever the provider adds to it (see the table further down). One extra space or line break gives a completely different result.

### Step 2: Enter the Secret Key

Type or paste the shared secret into Secret Key. The tool uses the key exactly as typed, as UTF-8 text. That is what GitHub, Stripe and Slack expect: Slack's documentation says you do not need to convert the signing secret to binary and should treat it as a standard UTF-8 string. If a service tells you to decode its key from hex or Base64 into raw bytes before signing, the tool's result will not match that service.

### Step 3: Choose the Algorithm

The default is HMAC-SHA256, which is what most webhook providers use. Pick HMAC-SHA1 only when a legacy system asks for it, and SHA-384 or SHA-512 when the receiver expects them. The algorithm must be the same on both sides; there is no "stronger" choice that still matches.

### Step 4: Copy the Signature

The HMAC Signature panel updates as you type, once both Message and Secret Key are filled in. Click Copy to take the lowercase hex value. If the value you are checking has a prefix such as `sha256=` or `v0=`, add the prefix yourself or remove it from the other value before you compare.

## A Test Vector You Can Reproduce

GitHub publishes a test pair for its webhook signatures. Enter these values with HMAC-SHA256:

- Message: `Hello, World!`
- Secret Key: `It's a Secret to Everybody`

The tool shows:

```text
757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17
```

That is the value in [GitHub's documentation](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries), where the header form is `sha256=757107ea…3e17`. Now press Enter after `Hello, World!` so the message ends with a line break. The signature becomes `8fde2e970f9163923fb1cb61bb945626ff2b4091d87e622ee3ad600160592325`. Every character counts, including the invisible ones. We checked both values with OpenSSL's `openssl dgst -sha256 -hmac` as well.

## How Webhook Providers Build the Signed Message

Most providers do not sign the body alone. Here is what three common ones put into the HMAC, according to their own documentation:

| Provider | Header | Message to paste into the tool | Algorithm | Header value |
|----------|--------|-------------------------------|-----------|--------------|
| [GitHub](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries) | `X-Hub-Signature-256` | The raw body | HMAC-SHA256 | `sha256=` + hex |
| [Stripe](https://docs.stripe.com/webhooks#verify-manually) | `Stripe-Signature` | The `t` value, a `.`, then the raw body | HMAC-SHA256 | `t=…,v1=` + hex |
| [Slack](https://docs.slack.dev/authentication/verifying-requests-from-slack) | `X-Slack-Signature` | `v0:`, the `X-Slack-Request-Timestamp` value, `:`, then the raw body | HMAC-SHA256 | `v0=` + hex |

So a Stripe event delivered with `t=1492774577` and the body `{"id":"evt_1"}` is checked by signing this exact message with your `whsec_…` endpoint secret:

```text
1492774577.{"id":"evt_1"}
```

Compare the tool's output with the `v1` value only. Stripe's documentation says the `v0` signature it sends with test events is fake, and that you should ignore every scheme except `v1`. GitHub still sends an older `X-Hub-Signature` header made with HMAC-SHA1, which it keeps only for legacy purposes.

The timestamp in Stripe's and Slack's message is there to stop replay attacks: an old request with a valid signature should still be refused. Slack's example rejects requests more than five minutes from local time, and Stripe's libraries use a default tolerance of five minutes. To read a timestamp header as a date, paste it into the [Unix Timestamp Converter](/developers/unix-timestamp/).

## Why Your HMAC Does Not Match

When the tool's value and the header's value differ, the cause is almost always one of these six.

### 1. The body was parsed before it was signed

Frameworks often turn a JSON body into an object and back, which reorders keys or drops whitespace. The provider signed the bytes it sent, not your re-serialized copy. Stripe's documentation says the body must be "the exact UTF-8 string Stripe sent, without any modifications". Log the raw body and paste that.

### 2. A line ending changed

Copying a body from a terminal or log can add or drop a final newline, or turn `\r\n` into `\n`. Browsers store the line breaks in a text box as `\n`, as the [HTML standard](https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element) describes, so a body with Windows line endings cannot be reproduced by pasting it into any web form. Check the signature in code for such bodies.

### 3. The wrong secret

Test and live endpoints have different secrets, and so do two endpoints of the same account. Stripe notes that the secret the Stripe CLI prints for forwarded events is not the one shown for a Dashboard endpoint. A space copied at the end of the key changes the result as surely as a wrong key: `It's a Secret to Everybody ` with a trailing space gives `587de830…7bf2d` instead of `757107ea…3e17`.

### 4. The prefix or timestamp is missing

Signing the body alone when the provider signs `timestamp.body` (Stripe) or `v0:timestamp:body` (Slack) can never match. Comparing `sha256=757…` with `757…` fails too. Build the message from the table above and strip the prefix before comparing.

### 5. A different algorithm or encoding

An HMAC-SHA1 value is 40 hex characters and an HMAC-SHA256 value is 64. If the lengths differ, the algorithms differ. If the header value contains `+`, `/` or ends in `=`, it is Base64 rather than hex, and the same bytes will look completely different from the tool's hex output.

### 6. Comparing by eye

Two 64-character values can differ in one character in the middle. Paste both into the [Diff Checker](/text/diff-checker/) to see the exact position. In code, never compare with `==`: GitHub recommends a constant-time comparison such as `crypto.timingSafeEqual` in Node.js, and Python has [`hmac.compare_digest`](https://docs.python.org/3/library/hmac.html#hmac.compare_digest) for the same reason, to avoid leaking how many characters matched through timing.

## How Long Should an HMAC Key Be?

[RFC 2104](https://www.rfc-editor.org/rfc/rfc2104#section-3) allows a key of any length but says that a key shorter than the hash output is "strongly discouraged as it would decrease the security strength of the function". For HMAC-SHA256 that means at least 32 bytes of random data. [RFC 7518](https://www.rfc-editor.org/rfc/rfc7518#section-3.2), which defines the JWT algorithms, makes the same size a requirement for HS256: a key of 256 bits or larger MUST be used.

To create one, set the [Random Key Generator](/security/random-key-generator/) to Hex and a length of 64 characters, which carries 256 bits of randomness. Keep the key in your server's configuration, never in front-end code or a public repository.

## HMAC vs Hash vs Digital Signature

| | Plain hash (SHA-256) | HMAC-SHA256 | Digital signature (RSA, ECDSA) |
|--|---------------------|-------------|--------------------------------|
| Needs a key | No | One shared secret | A private key to sign, a public key to verify |
| Detects changes | Yes | Yes | Yes |
| Proves who sent it | No | Anyone with the shared key | Only the private key holder |
| Typical use | Checksums, file integrity | Webhooks, API request signing, HS256 JWTs | Certificates, RS256 and ES256 JWTs, signed software |
| FindUtils tool | [MD5 & SHA Hash Generator](/security/md5-hash-generator/) | [HMAC Generator](/security/hmac-generator/) | [JWT Decoder](/developers/jwt-decoder/) verifies RS256 tokens |

The HS256 algorithm of JSON Web Tokens is HMAC with SHA-256 ([RFC 7518](https://www.rfc-editor.org/rfc/rfc7518#section-3.2)). The [JWT Decoder](/developers/jwt-decoder/) verifies and signs HS256, HS384 and HS512 tokens on the page, so use it rather than rebuilding the token's signing input by hand.

## Common Mistakes to Avoid

### Mistake 1: Pasting a live production secret into a web page

The tool computes the HMAC in your browser, and nothing you type is sent from the page. Even so, the safest habit is to debug with a test endpoint's secret, or to rotate a secret after it has been copied around during an incident. GitHub, Stripe and Slack all let you regenerate a signing secret.

### Mistake 2: Using HMAC to store passwords

HMAC is fast by design, which makes it a poor password hash. Use a slow, salted algorithm instead, as explained in the [bcrypt vs Argon2id guide](/guides/password-hasher/).

### Mistake 3: Treating HMAC as encryption

An HMAC does not hide the message; it is sent next to the message in plain view. It only proves the message was not changed. To keep the content secret as well, encrypt it.

### Mistake 4: Skipping the timestamp check

A valid signature on an old request is still a valid signature. If the provider sends a timestamp, check that it is recent before you accept the event.

## Tools Used in This Guide

- **[HMAC Generator](/security/hmac-generator/)**: Compute HMAC-SHA1, SHA-256, SHA-384 or SHA-512 signatures as hex
- **[Random Key Generator](/security/random-key-generator/)**: Create a random hex key of the right length for signing
- **[MD5 & SHA Hash Generator](/security/md5-hash-generator/)**: Compute plain hashes when no key is involved
- **[JWT Decoder](/developers/jwt-decoder/)**: Decode, verify and sign HS256 tokens
- **[Unix Timestamp Converter](/developers/unix-timestamp/)**: Read the timestamp in a signature header as a date
- **[Diff Checker](/text/diff-checker/)**: Find the one character where two signatures differ
- **[2FA Code Tester](/security/two-fa-code-tester/)**: See HMAC-SHA1 at work in the six-digit codes of authenticator apps

## FAQ

### How do I generate an HMAC-SHA256 online?

Open the FindUtils HMAC Generator, paste the message, type the secret key, and keep the algorithm on HMAC-SHA256. The 64-character hex signature appears as you type; click Copy to use it.

### How do I verify a webhook signature?

Rebuild the exact message the provider signed (for GitHub the raw body, for Stripe `timestamp.body`, for Slack `v0:timestamp:body`), compute the HMAC-SHA256 with your signing secret, and compare it with the hex value in the header, without its prefix. In production code, use a constant-time comparison.

### Why does my HMAC not match?

Usually because the body was changed before signing (parsed JSON, a line ending), the secret is wrong or has a trailing space, or the provider's timestamp or prefix was left out. Compare the two values in a diff tool to see where they differ.

### What is the difference between HMAC and SHA-256?

SHA-256 is a hash anyone can compute. HMAC-SHA256 mixes a secret key into SHA-256, so only someone who holds the key can produce or check the value.

### Is HMAC-SHA256 the same as HS256?

Yes. HS256 is the JSON Web Token name for HMAC using SHA-256, as defined in RFC 7518.

### Can an HMAC be reversed?

No. You cannot get the message or the key back from an HMAC. It is a check value, not encryption.

### Is my secret key uploaded?

No. On the tool page the HMAC is computed in your browser, and the key and message you type are not sent to a server.

## Next Steps

- See how authenticator apps turn HMAC-SHA1 into six-digit codes in [how 2FA codes work](/blog/how-2fa-codes-work-totp-explained/).
- Learn what plain hashes can and cannot prove in the [hash text and files guide](/guides/how-to-hash-text-data-online/).
- Read and check tokens with the [JWT decoder guide](/guides/jwt-decoder-guide/).
- Browse every security tool in the [complete guide to online security tools](/guides/complete-guide-to-online-security-tools/).

## Sources

- [RFC 2104: HMAC: Keyed-Hashing for Message Authentication](https://www.rfc-editor.org/rfc/rfc2104)
- [RFC 4231: Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512](https://www.rfc-editor.org/rfc/rfc4231)
- [RFC 7518: JSON Web Algorithms (JWA)](https://www.rfc-editor.org/rfc/rfc7518)
- [GitHub Docs: Validating webhook deliveries](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries)
- [Stripe Docs: Receive Stripe events in your webhook endpoint](https://docs.stripe.com/webhooks#verify-manually)
- [Slack Developer Docs: Verifying requests from Slack](https://docs.slack.dev/authentication/verifying-requests-from-slack)
- [Python documentation: hmac.compare_digest](https://docs.python.org/3/library/hmac.html#hmac.compare_digest)
- [HTML Standard: The textarea element](https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element)
