If you've ever needed to generate an HMAC signature to validate a webhook, MD5-hash a file for deduplication, or AES-encrypt a field before storing it, the n8n Crypto node is the tool for the job — and it's built in, no credentials required.
This guide covers every operation the Crypto node supports, the gotchas that burn people, and three real workflow patterns you can drop in today.
What the n8n Crypto Node Does
The Crypto node exposes common cryptographic primitives directly inside your workflow:
| Operation | What it produces |
|---|---|
| Hash | One-way fingerprint (MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-3) |
| Hmac | Keyed hash for signature verification (same algorithm list + a secret key) |
| Sign | RSA or ECDSA signature over a payload |
| Encrypt | AES-256 CBC or ECB encryption of a string field |
| Decrypt | Reverse of Encrypt — decrypts a ciphertext field |
| Generate | Generates a UUID (v4) or random bytes |
Output
The result lands in a field you name on the output item — you choose the field name in the node settings. Input items pass through unchanged; only the new field is added.
Operation-by-Operation Breakdown
Hash
Use it to fingerprint content: detect duplicate records, build cache keys, or produce file checksums.
Settings:
- Type: MD5, SHA-256, SHA-512, etc.
- Value: the expression or literal to hash
-
Property Name: name of the output field (e.g.,
hash) -
Encoding:
hex(default) orbase64
Input: { "email": "user@example.com" }
Config: Type=SHA-256, Value={{ $json.email }}, Property Name=emailHash
Output: { "email": "user@example.com", "emailHash": "b4c9a..." }
MD5 note: MD5 is cryptographically broken — never use it for security. It's fine for deduplication fingerprints and ETL checksums where collision resistance doesn't matter.
Hmac
HMAC (Hash-based Message Authentication Code) is what you use to verify that a webhook payload came from the source that knows your secret — Stripe, GitHub, Shopify, and virtually every modern webhook provider use HMAC-SHA256.
Settings:
- Type: same algorithm list as Hash
-
Value: the raw payload string (often
{{ $json.body }}or the full stringified body) - Secret: your shared secret key
-
Property Name: output field (e.g.,
signature) -
Encoding:
hexorbase64
Sign
Signs a payload with a private key (RSA-SHA256, RSA-SHA512, or EC with named curves). Useful for generating JWTs manually or signing API requests that require asymmetric auth (e.g., Apple Pay, some banking APIs).
Gotcha: The key must be in PEM format. Load it from an environment variable or n8n credential — never hard-code it in the workflow.
Encrypt / Decrypt
AES-256 encryption for a string field. Both CBC and ECB modes are available.
- CBC (recommended): requires an initialization vector (IV); output is not deterministic — two encryptions of the same plaintext give different ciphertexts.
- ECB: no IV; deterministic but vulnerable to pattern analysis. Avoid for anything sensitive.
Gotcha: n8n's AES implementation expects the key to be exactly 32 characters (256 bits). If your key is shorter, pad it; if it's longer, truncate.
Generate
Produces a v4 UUID or a hex/base64 string of N random bytes. Handy for idempotency keys, one-time tokens, and unique record IDs.
Three Real Workflow Patterns
Pattern 1: Webhook Signature Verification
Stripe (and most webhook providers) include a header like Stripe-Signature containing an HMAC-SHA256 of the raw request body. You need to recompute it and compare.
[Webhook Node] → [Crypto Node (Hmac)] → [IF Node (signatures match?)] → [Process / Reject]
Crypto node config:
- Type:
SHA-256 - Value:
{{ $json.body }}(raw body string — use the Webhook node's "Raw Body" option) - Secret:
{{ $env.STRIPE_WEBHOOK_SECRET }} - Property Name:
computedSig - Encoding:
hex
Then in the IF node, compare computedSig to the value extracted from the incoming header (after stripping the sha256= prefix Stripe adds).
Free JSON:
{
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "stripe-events",
"options": { "rawBody": true }
}
},
{
"name": "Compute HMAC",
"type": "n8n-nodes-base.crypto",
"parameters": {
"action": "hmac",
"type": "SHA256",
"value": "={{ $json.body }}",
"secret": "={{ $env.STRIPE_WEBHOOK_SECRET }}",
"propertyName": "computedSig",
"encoding": "hex"
}
},
{
"name": "Verify Signature",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.computedSig }}",
"operation": "equal",
"value2": "={{ $json.headers['stripe-signature'].split(',').find(p => p.startsWith('v1=')).replace('v1=','') }}"
}
]
}
}
}
]
}
Pattern 2: Deduplication with Content Hash
When polling an RSS feed, scraper, or API that doesn't provide stable IDs, hash the content to detect duplicates before inserting into your database.
[HTTP Request / RSS] → [Crypto (SHA-256 of title+url)] → [Postgres: INSERT ... ON CONFLICT DO NOTHING]
Crypto node config:
- Type:
SHA-256 - Value:
{{ $json.title + $json.url }} - Property Name:
contentHash - Encoding:
hex
Your Postgres INSERT uses contentHash as the unique key: INSERT INTO items (hash, title, url) VALUES (...) ON CONFLICT (hash) DO NOTHING. Idempotent polling, zero duplicate rows.
Pattern 3: PII Pseudonymisation Before External Send
You need to send analytics events to a third-party service but can't share raw emails or user IDs.
[Trigger] → [Crypto (SHA-256 of email)] → [HTTP Request to analytics endpoint]
Hash the email to a consistent pseudonym: same user, same hash every time — good enough for cohort analysis, impossible to reverse without the original. Add a pepper (a secret appended to the value) if you want additional protection: {{ $json.email + $env.HASH_PEPPER }}.
Gotchas and Limits
1. The Crypto node works on strings, not binary files.
You can't feed it a Buffer or binary attachment. If you need to hash a file, first extract its content as base64 with the Read Binary File node, then hash the base64 string. Results match standard checksums when you decode first.
2. Encoding matters downstream.
The default output is hex. If your API expects base64, set it in the node — don't try to convert afterwards with an expression (it won't decode correctly).
3. Key length for AES.
AES-256 requires a 32-byte key. n8n does not pad or error — it silently produces wrong output if your key is the wrong length. Always verify key length before encrypting.
4. Generate UUID doesn't add node-level randomness.
Each item in the batch gets the same UUID generation call. If you're processing 100 items, each gets a distinct UUID (the node runs per item). This is correct behavior, but verify it with a quick test run if you're unsure.
5. Timing attacks.
The IF node's string comparison is not constant-time. For production webhook signature verification in high-security contexts, you'd want a Code node with crypto.timingSafeEqual(). For most business automation use cases, the IF node comparison is fine.
Get the Complete Workflow Pack
These patterns are part of the n8n Workflow Starter Pack — 20+ production-ready workflows covering webhooks, APIs, databases, AI, and more, all with setup docs.
👉 Get the n8n Workflow Starter Pack ($29)
Quick Reference
| Need | Operation | Algorithm |
|---|---|---|
| Fingerprint for dedup | Hash | SHA-256 |
| Webhook signature verify | Hmac | SHA-256 |
| Store PII safely | Hash | SHA-256 + pepper |
| Encrypt a field | Encrypt | AES-256-CBC |
| Generate unique ID | Generate | UUID v4 |
| File checksum (legacy) | Hash | MD5 (non-security only) |
The Crypto node is small but it's the foundation for secure data handling in n8n. HMAC verification alone makes it essential if you're receiving webhooks from any major payment or event provider.
Drop a comment below: are you using HMAC for webhook verification, or is there another crypto use case you'd like covered?
Top comments (1)
Are you using the Crypto node for HMAC webhook verification, dedup hashing, or something else? For me the HMAC pattern for Stripe webhooks is the most common use case — drop your use case below.