EncryptCodecencryptcodec
Blog/Security
SecurityMarch 12, 2026 · 7 min read

HMAC vs Digital Signatures — When Each One Is the Right Tool

You need to verify that a message hasn't been tampered with. You've heard of HMAC and digital signatures. Both solve authenticity problems — but they're not interchangeable. Using the wrong one is a security mistake, not just a style preference.

What HMAC Actually Does

HMAC (Hash-based Message Authentication Code) combines a secret key with a hash function (typically SHA-256) to produce a fixed-length tag. The receiver recomputes the tag using the same key and compares. If they match, the message is authentic and unmodified.

The critical point: anyone who can verify an HMAC can also create one. There's no separation of concerns between signing and verifying — both require the same secret.

This makes HMAC ideal when:

  • You control both endpoints (e.g., your server signs, your server verifies)
  • You're authenticating webhook payloads
  • You're building stateless session tokens for internal use
  • Speed matters and you're doing high-volume request signing
import { createHmac } from "crypto";

const secret = "super-secret-key";
const payload = JSON.stringify({ userId: 42, action: "delete" });

// Sign
const hmac = createHmac("sha256", secret)
.update(payload)
.digest("hex");

// Verify (timing-safe comparison recommended in production)
import { timingSafeEqual } from "crypto";

function verifyHmac(payload, receivedHmac, secret) {
const expected = createHmac("sha256", secret)
  .update(payload)
  .digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(receivedHmac));
}

What Digital Signatures Actually Do

Digital signatures use asymmetric cryptography. The sender signs with a private key. Anyone with the corresponding public key can verify the signature — but cannot forge one.

This separation is what enables non-repudiation: if you signed it, only you could have done so. The verifier can't produce a valid signature on your behalf.

Use digital signatures when:

  • You need to prove who signed, not just that it was signed
  • External parties need to verify without sharing a secret
  • You're issuing JWTs with RS256/ES256 (public key verification by third parties)
  • You're signing documents, software artifacts, or legal records
import { generateKeyPairSync, createSign, createVerify } from "crypto";

// Generate RSA key pair (do this once, store securely)
const { privateKey, publicKey } = generateKeyPairSync("rsa", {
modulusLength: 2048,
});

const payload = JSON.stringify({ userId: 42, action: "delete" });

// Sign with private key
const signer = createSign("SHA256");
signer.update(payload);
const signature = signer.sign(privateKey, "base64");

// Verify with public key (safe to distribute)
const verifier = createVerify("SHA256");
verifier.update(payload);
const isValid = verifier.verify(publicKey, signature, "base64");
console.log(isValid); // true

The Key Decision: Do Both Sides Need to Be Trusted Equally?

This is the real question. Here are common scenarios mapped to the right tool:

Use HMAC when:

  • Webhook verification — Stripe, GitHub, Twilio all use HMAC-SHA256. Your server holds the secret; you verify incoming payloads.
  • Internal service-to-service auth — both services are yours, both know the secret.
  • Signed cookies or session tokens — your own backend signs and verifies.
  • API request signing — AWS Signature v4 is HMAC-based.

Use Digital Signatures when:

  • JWTs for third-party consumption — RS256 or ES256 lets any service verify your token with your public key.
  • Code signing / release artifacts — you publish the public key; users verify your binary was untampered.
  • Legal or audit records — you need to prove a specific private key holder signed a document.
  • OAuth / OpenID Connect — identity providers sign tokens with their private key; relying parties verify with the public key from the JWKS endpoint.

The Non-Repudiation Trap

A common mistake: using HMAC for audit logs or legal records. If Alice and Bob share an HMAC secret, and you find an HMAC-signed record, you cannot prove Alice created it — Bob could have too. Either party can deny it.

Digital signatures solve this. Only the holder of the private key could have produced a valid signature. That's what non-repudiation means.

Performance Reality Check

HMAC-SHA256 is extremely fast — often 10–100x faster than RSA-2048 signing. For high-throughput systems doing millions of request verifications, this matters. ECDSA (P-256) is significantly faster than RSA and is the modern choice when you do need asymmetric signatures.

If you're signing 50,000 JWTs per second, RS256 will hurt. ES256 is a reasonable middle ground. But if non-repudiation isn't a requirement, HS256 (HMAC) is the correct and fastest choice.

If you control both ends → HMAC. It's simpler, faster, and perfectly secure for internal authentication.

If the verifier is someone else, or you need proof of origin → Digital signatures. The extra complexity of key pairs is the price of non-repudiation and public verifiability.

Don't reach for asymmetric cryptography because it "sounds more secure." HMAC with a proper

Share this post

Generate and verify HMACs instantly

Free, browser-based — no signup required.

Frequently Asked Questions

Related posts