All posts

Encryption Tools Explained: AES vs BCrypt vs HMAC

July 22, 2026 · DevTools

encryption
aes
bcrypt
hmac
security
developer-tools

"Encrypt the password" is the phrase that launches a thousand security incidents. Passwords aren't encrypted — they're hashed. And API requests aren't encrypted either — they're signed. AES, BCrypt, and HMAC are three different tools for three different jobs, and confusing them is how data ends up exposed. This guide draws the lines clearly.

All three tools mentioned run entirely in your browser — try every example without sending a byte anywhere.

The one-paragraph version of each

  • AES (encryption)reversible secrecy. You have data you must get back later (a message, a file), and only key-holders may read it.
  • BCrypt (password hashing)irreversible verification. You must check a password later without ever storing it.
  • HMAC (message authentication)proof of origin and integrity. You must know a message came from a key-holder and wasn't altered.

If you remember one sentence: encrypt what you must read again, hash what you must verify, sign what you must trust.

AES: when you need the data back

AES (Advanced Encryption Standard) is the world's workhorse cipher — TLS, disk encryption, and most "encrypted at rest" claims rest on it. It takes a key and plaintext, and produces ciphertext that only the same key can reverse.

The mode matters enormously:

  • AES-GCM (Galois/Counter Mode) — the modern default. It encrypts and authenticates: any tampering with the ciphertext makes decryption fail loudly. Use this.
  • AES-CBC — the older mode. Encrypts but doesn't authenticate; without a separate MAC, attackers can flip bits undetected (the classic "padding oracle" family of bugs).

In practice you rarely handle raw AES keys — you derive one from a passphrase using a slow KDF like PBKDF2, plus a random salt and IV per message. That's exactly what the AES Encryption tool does: passphrase in, AES-GCM encryption via the Web Crypto API, all local.

passphrase + salt ──PBKDF2──► key ──AES-GCM──► ciphertext + auth tag

BCrypt: when you must never store the thing

Storing passwords is a paradox: you need to verify them, but storing them (even encrypted) means a breach exposes them. The answer is slow, salted, one-way hashing:

  • One-way — you can't recover the password from the hash; verification means hashing the attempt and comparing
  • Salted — a random per-user salt (built into BCrypt's output) kills rainbow tables and makes identical passwords hash differently
  • Slow — the tunable cost factor makes each guess expensive, so brute force on a leaked database crawls

This is why "encrypting passwords" is wrong: anything encrypted can be decrypted with the key, and the key lives on the same server that just got breached. Hash instead — the BCrypt Generator produces and verifies BCrypt hashes with adjustable cost. (For the full landscape — MD5, SHA-256, Argon2 — see Hash Generators Explained.)

HMAC: when you need to know who sent it

Encryption hides a message; it doesn't prove who wrote it or that it arrived intact. HMAC (Hash-based Message Authentication Code) solves that: combine a secret key with the message through a hash function, and the output signature proves both authenticity (only key-holders can produce it) and integrity (any change invalidates it).

You meet HMAC constantly:

  • Webhook signatures — Stripe and GitHub sign every webhook; your endpoint recomputes the HMAC to reject forgeries
  • HS256 JWTs — the most common JWT algorithm is HMAC-SHA256 over the header and payload
  • API request signing — AWS-style signatures authenticate each call

The HMAC Generator signs any message with SHA-1/256/384/512 and outputs hex or Base64 — handy for reproducing a webhook signature while debugging. One warning from the field: a plain hash(secret + message) is not equivalent (length-extension attacks); always use real HMAC, and compare signatures with a constant-time comparison on the server.

Worked example: verifying a webhook signature

The HMAC workflow end to end, as your endpoint sees it:

  1. Stripe sends POST /webhook with the raw body and a Stripe-Signature header
  2. You compute HMAC-SHA256(secret, timestamp + "." + rawBody) locally
  3. Compare with the header using a constant-time comparison
  4. Match → process the event; mismatch → 401 and log

The critical detail: sign the raw, unparsed body. Parsing and re-serializing JSON can reorder keys or change whitespace, which changes the bytes and breaks the signature — a classic first-integration bug that sends people hunting for hours.

A five-second rule of thumb

Still unsure which one your task needs? Ask one question: "Do I ever need the original value back?" Yes → AES (encryption). No → hashing: BCrypt for passwords, plain SHA-256 for fingerprints. And if the question is instead "did this message really come from them?" → HMAC. Three questions, three tools, zero overlap.

Where keys live (and don't)

All three primitives hinge on key hygiene:

  • Never in code or git — environment variables, a secrets manager, or a KMS; rotate on schedule and on suspicion
  • Per-environment secrets — a leaked dev key should never open prod
  • Passphrases vs keys — humans remember passphrases; machines hold raw keys. Derive keys from passphrases with PBKDF2/Argon2 (as the AES Encryption tool does), don't use the passphrase as the key directly

Encoding vs encryption: don't mix them up

One more confusion worth killing while we're here: Base64 and its cousins are encodings, not encryption. QWxhZGluOk9wZW5TZXNhbWU= is readable by anyone in one step — it protects data from transport damage, not from readers. If a system claims data is "encrypted" but you can decode it without any key, it's encoded. Real encryption always requires a key; see Base64 Encoding Explained for where encodings genuinely belong.

What about public-key crypto?

AES, BCrypt, and HMAC are all symmetric — one shared secret. Sometimes that's impossible: verifying JWTs from an identity provider (RS256), encrypting for someone whose secret you don't share. That's the public-key world (RSA, Ed25519): a private key signs or decrypts, a public key verifies or encrypts. It's a bigger topic — the short version is "use HS256/HMAC when you control both ends, RS256/public-key when you don't." Our JWT guide walks through both.

The decision table

Your situationUseTool
Store a secret you must read laterAES-GCMAES Encryption
Store user passwordsBCrypt (or Argon2)BCrypt Generator
Verify a webhook/API message is genuineHMAC-SHA256HMAC Generator
Fingerprint a file / checksumPlain SHA-256 (no key needed)Hash Generator
"Encrypt" something you never need back❌ — you want hashing, not encryptionBCrypt Generator

The mistakes that keep repeating

  1. Encrypting passwords — reversible means breachable; hash with BCrypt instead
  2. Using AES-CBC without authentication — use GCM, or CBC + HMAC
  3. Rolling your own "HMAC" with string concatenation — length-extension territory
  4. Reusing salts/IVs — every encryption gets a fresh random salt and IV
  5. Fast hashes for passwords — SHA-256 runs billions of times per second on attacker hardware

Everything stays in your browser

Keys, passphrases, webhook secrets — the exact things you should never paste into a server-side tool. All three tools compute locally via the Web Crypto API: nothing is transmitted, logged, or stored.

Next steps