Skip to content

Node.js Crypto Module — Complete Guide to Hash, HMAC, Cipher, and Sign

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Node.js Crypto Module. We cover key concepts, practical examples, and best practices to help you master this topic.

Node.js crypto module provides cryptographic functionality including hash functions, HMAC, symmetric and asymmetric encryption, digital signatures, and secure random number generation.

What You'll Learn

By the end of this tutorial, you'll hash passwords with salt, encrypt and decrypt data with AES, sign and verify with RSA, generate secure random values, and use HMAC for message authentication.

Why Crypto Matters

Cryptography protects sensitive data at rest and in transit. Node.js applications handle passwords, API keys, tokens, and personal data that must be encrypted or hashed.

Real-World Use

A user management system hashes passwords with bcrypt, encrypts PII fields with AES-256-GCM, signs JWTs with RSA, and uses HMAC for Webhook payload verification.

Crypto Path

flowchart LR
  A[Security Checklist] --> B[Crypto]
  B --> C[JWT]
  C --> D[OAuth]
  D --> E[Deployment]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Hashing with SHA-256

Hash data using SHA-256 for integrity verification. Hashes are one-way and deterministic.

const crypto = require("node:crypto");
function hashData(data) {
  return crypto.createHash("sha256").update(data).digest("hex");
}
const message = "Hello Node.js Crypto";
const hash = hashData(message);
console.log("SHA-256 hash:", hash);
console.log("Length:", hash.length, "characters");
// Verify integrity
console.log("Match:", hashData(message) === hash);

HMAC for Message Authentication

HMAC uses a secret key with the hash to verify both data integrity and authenticity.

const crypto = require("node:crypto");
function createHMAC(data, secret) {
  return crypto.createHmac("sha256", secret).update(data).digest("hex");
}
const secret = "my-secret-key";
const payload = JSON.stringify({ userId: 123, action: "transfer" });
const hmac = createHMAC(payload, secret);
console.log("HMAC:", hmac);
function verifyHMAC(data, secret, expected) {
  return crypto.timingSafeEqual(Buffer.from(createHMAC(data, secret)), Buffer.from(expected));
}
console.log("Verified:", verifyHMAC(payload, secret, hmac));

AES Encryption and Decryption

Encrypt data with AES-256-GCM for authenticated encryption (confidentiality + integrity).

const crypto = require("node:crypto");
function encrypt(text, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  let encrypted = cipher.update(text, "utf8", "hex");
  encrypted += cipher.final("hex");
  const authTag = cipher.getAuthTag().toString("hex");
  return { encrypted, iv: iv.toString("hex"), authTag };
}
function decrypt(encrypted, key, ivHex, authTagHex) {
  const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(ivHex, "hex"));
  decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
  let decrypted = decipher.update(encrypted, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

RSA Signing and Verification

Use asymmetric RSA keys for digital signatures: private key signs, public key verifies.

const crypto = require("node:crypto");
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
  modulusLength: 2048,
  publicKeyEncoding: { type: "spki", format: "pem" },
  privateKeyEncoding: { type: "pkcs8", format: "pem" },
});
function sign(data, key) {
  return crypto.createSign("sha256").update(data).sign(key, "base64");
}
function verify(data, signature, key) {
  return crypto.createVerify("sha256").update(data).verify(key, signature, "base64");
}
const data = "Important document";
const signature = sign(data, privateKey);
console.log("Verified:", verify(data, signature, publicKey));

Password Hashing with Scrypt

Use scrypt for secure password hashing with configurable cost parameters.

const crypto = require("node:crypto");
function hashPassword(password) {
  const salt = crypto.randomBytes(32).toString("hex");
  const hash = crypto.scryptSync(password, salt, 64).toString("hex");
  return `${salt}:${hash}`;
}
function verifyPassword(password, stored) {
  const [salt, hash] = stored.split(":");
  const verify = crypto.scryptSync(password, salt, 64).toString("hex");
  return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(verify));
}
const hashed = hashPassword("user-password-123");
console.log("Verified:", verifyPassword("user-password-123", hashed));

Secure Random Values

Generate cryptographically secure random values for tokens, keys, and IDs.

const crypto = require("node:crypto");
const randomBytes = crypto.randomBytes(32);
console.log("Random bytes (hex):", randomBytes.toString("hex"));
const randomUUID = crypto.randomUUID();
console.log("Random UUID:", randomUUID);
const randomInt = crypto.randomInt(100000, 999999);
console.log("Random 6-digit code:", randomInt);

Common Mistakes

1. Using MD5 or SHA1 for Passwords

These are fast and vulnerable to rainbow table attacks. Use bcrypt, scrypt, or argon2.

2. Storing Encryption Keys in Code

Keys in source code are exposed in version control. Use environment variables or a KMS.

3. Reusing IVs in AES Encryption

AES-GCM with a reused IV breaks confidentiality. Always generate a random IV for each encryption.

4. Not Using Timing-Safe Comparison

String comparison of hashes is vulnerable to timing attacks. Use crypto.timingSafeEqual.

5. ECB Mode for Encryption

ECB mode encrypts identical blocks identically, leaking patterns. Always use GCM or CBC.

Practice Questions

1. What is the difference between hashing and encryption?

Hashing is one-way and irreversible. Encryption is two-way: data can be decrypted with a key.

2. What is HMAC used for?

Message authentication: verifying both data integrity and authenticity using a shared secret key.

3. Why use AES-GCM over AES-CBC?

GCM provides authenticated encryption (confidentiality + integrity) in one operation. CBC requires a separate MAC.

4. What is a digital signature?

A hash encrypted with a private key. Anyone with the public key can verify it was signed by the private key holder.

5. Challenge: Implement a utility that encrypts a JSON payload with AES-256-GCM and signs with HMAC.

function secureEncode(payload, encryptionKey, hmacKey) {
  const json = JSON.stringify(payload);
  const { encrypted, iv, authTag } = encrypt(json, encryptionKey);
  const signature = createHMAC(encrypted + iv + authTag, hmacKey);
  return { encrypted, iv, authTag, signature };
}

FAQ

What is the best algorithm for password hashing?

argon2id is the current gold standard. bcrypt and scrypt are also acceptable. Avoid MD5, SHA1, and SHA256 for passwords.

Is crypto.randomBytes cryptographically secure?

Yes. It uses the operating system's CSPRNG (CryptGenRandom on Windows, /dev/urandom on Unix).

What key size should I use for AES?

AES-256 uses 32-byte keys. AES-128 uses 16-byte keys. AES-256 is recommended for security.

How do I generate an RSA key pair?

Use crypto.generateKeyPairSync with modulusLength: 2048 or 4096. Export in PEM format.

What is the difference between symmetric and asymmetric encryption?

Symmetric uses one key (AES). Asymmetric uses key pairs (RSA). Symmetric is faster, asymmetric solves key exchange.

Mini Project: Encrypted Configuration Store

Build a utility that encrypts and decrypts configuration values at rest.

const crypto = require("node:crypto");
class SecureConfig {
  constructor(key) {
    this.key = crypto.scryptSync(key, "config-salt", 32);
  }
  encrypt(value) {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv("aes-256-gcm", this.key, iv);
    let enc = cipher.update(JSON.stringify(value), "utf8", "hex");
    enc += cipher.final("hex");
    return JSON.stringify({ data: enc, iv: iv.toString("hex"), tag: cipher.getAuthTag().toString("hex") });
  }
  decrypt(stored) {
    const { data, iv, tag } = JSON.parse(stored);
    const decipher = crypto.createDecipheriv("aes-256-gcm", this.key, Buffer.from(iv, "hex"));
    decipher.setAuthTag(Buffer.from(tag, "hex"));
    let dec = decipher.update(data, "hex", "utf8");
    dec += decipher.final("utf8");
    return JSON.parse(dec);
  }
}

What's Next

Node.js JWT Authentication Node.js OAuth Node.js Security Checklist

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro