Node.js Crypto Module — Complete Guide to Hash, HMAC, Cipher, and Sign
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
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