Back to Blog
Node.jscryptoSecurityEncryption

Node.js crypto Module Guide

Master the Node.js crypto module for hashing, encryption, and secure random generation.

B
Bootspring Team
Engineering
December 14, 2018
6 min read

The crypto module provides cryptographic functionality including hashing, encryption, and secure random generation. Here's how to use it.

Hashing#

1import crypto from 'node:crypto'; 2 3// Simple hash 4function hash(data, algorithm = 'sha256') { 5 return crypto.createHash(algorithm).update(data).digest('hex'); 6} 7 8console.log(hash('Hello, World!')); // SHA-256 hash 9console.log(hash('Hello, World!', 'md5')); // MD5 hash 10console.log(hash('Hello, World!', 'sha512')); // SHA-512 hash 11 12// Hash with different encodings 13const hashBuffer = crypto.createHash('sha256') 14 .update('Hello') 15 .digest(); // Buffer 16 17const hashBase64 = crypto.createHash('sha256') 18 .update('Hello') 19 .digest('base64'); // Base64 string 20 21// Incremental hashing 22const hasher = crypto.createHash('sha256'); 23hasher.update('Hello'); 24hasher.update(', '); 25hasher.update('World!'); 26console.log(hasher.digest('hex'));

HMAC (Hash-based Message Authentication)#

1import crypto from 'node:crypto'; 2 3// Create HMAC 4function createHmac(data, secret, algorithm = 'sha256') { 5 return crypto.createHmac(algorithm, secret) 6 .update(data) 7 .digest('hex'); 8} 9 10const secret = 'my-secret-key'; 11const hmac = createHmac('message to authenticate', secret); 12console.log(hmac); 13 14// Verify HMAC 15function verifyHmac(data, secret, providedHmac) { 16 const calculated = createHmac(data, secret); 17 return crypto.timingSafeEqual( 18 Buffer.from(calculated), 19 Buffer.from(providedHmac) 20 ); 21}

Password Hashing#

1import crypto from 'node:crypto'; 2 3// Hash password with salt (using scrypt) 4async function hashPassword(password) { 5 return new Promise((resolve, reject) => { 6 const salt = crypto.randomBytes(16).toString('hex'); 7 8 crypto.scrypt(password, salt, 64, (err, derivedKey) => { 9 if (err) reject(err); 10 resolve(`${salt}:${derivedKey.toString('hex')}`); 11 }); 12 }); 13} 14 15// Verify password 16async function verifyPassword(password, stored) { 17 return new Promise((resolve, reject) => { 18 const [salt, hash] = stored.split(':'); 19 20 crypto.scrypt(password, salt, 64, (err, derivedKey) => { 21 if (err) reject(err); 22 resolve(crypto.timingSafeEqual( 23 Buffer.from(hash, 'hex'), 24 derivedKey 25 )); 26 }); 27 }); 28} 29 30// Usage 31const hashed = await hashPassword('myPassword123'); 32console.log('Hashed:', hashed); 33 34const isValid = await verifyPassword('myPassword123', hashed); 35console.log('Valid:', isValid); // true

Random Bytes#

1import crypto from 'node:crypto'; 2 3// Synchronous 4const randomSync = crypto.randomBytes(32); 5console.log(randomSync.toString('hex')); 6 7// Asynchronous 8crypto.randomBytes(32, (err, buffer) => { 9 if (err) throw err; 10 console.log(buffer.toString('hex')); 11}); 12 13// Promise wrapper 14function randomBytesAsync(size) { 15 return new Promise((resolve, reject) => { 16 crypto.randomBytes(size, (err, buffer) => { 17 if (err) reject(err); 18 else resolve(buffer); 19 }); 20 }); 21} 22 23// Generate random string 24function randomString(length) { 25 return crypto.randomBytes(Math.ceil(length / 2)) 26 .toString('hex') 27 .slice(0, length); 28} 29 30// Random integer in range 31function randomInt(min, max) { 32 return crypto.randomInt(min, max); 33} 34 35console.log(randomInt(1, 100)); // Random number 1-99

UUID Generation#

1import crypto from 'node:crypto'; 2 3// Generate UUID v4 4const uuid = crypto.randomUUID(); 5console.log(uuid); // e.g., '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed' 6 7// Batch generation 8function generateUUIDs(count) { 9 return Array.from({ length: count }, () => crypto.randomUUID()); 10}

Symmetric Encryption (AES)#

1import crypto from 'node:crypto'; 2 3const algorithm = 'aes-256-gcm'; 4 5// Encrypt 6function encrypt(text, key) { 7 const iv = crypto.randomBytes(16); 8 const cipher = crypto.createCipheriv(algorithm, key, iv); 9 10 let encrypted = cipher.update(text, 'utf8', 'hex'); 11 encrypted += cipher.final('hex'); 12 13 const authTag = cipher.getAuthTag(); 14 15 return { 16 iv: iv.toString('hex'), 17 encrypted, 18 authTag: authTag.toString('hex'), 19 }; 20} 21 22// Decrypt 23function decrypt(encrypted, key) { 24 const decipher = crypto.createDecipheriv( 25 algorithm, 26 key, 27 Buffer.from(encrypted.iv, 'hex') 28 ); 29 30 decipher.setAuthTag(Buffer.from(encrypted.authTag, 'hex')); 31 32 let decrypted = decipher.update(encrypted.encrypted, 'hex', 'utf8'); 33 decrypted += decipher.final('utf8'); 34 35 return decrypted; 36} 37 38// Usage 39const key = crypto.randomBytes(32); // 256 bits 40const message = 'Secret message'; 41 42const encrypted = encrypt(message, key); 43console.log('Encrypted:', encrypted); 44 45const decrypted = decrypt(encrypted, key); 46console.log('Decrypted:', decrypted);

Asymmetric Encryption (RSA)#

1import crypto from 'node:crypto'; 2 3// Generate key pair 4function generateKeyPair() { 5 return crypto.generateKeyPairSync('rsa', { 6 modulusLength: 2048, 7 publicKeyEncoding: { 8 type: 'spki', 9 format: 'pem', 10 }, 11 privateKeyEncoding: { 12 type: 'pkcs8', 13 format: 'pem', 14 }, 15 }); 16} 17 18const { publicKey, privateKey } = generateKeyPair(); 19 20// Encrypt with public key 21function rsaEncrypt(data, publicKey) { 22 return crypto.publicEncrypt( 23 { 24 key: publicKey, 25 padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, 26 oaepHash: 'sha256', 27 }, 28 Buffer.from(data) 29 ); 30} 31 32// Decrypt with private key 33function rsaDecrypt(encrypted, privateKey) { 34 return crypto.privateDecrypt( 35 { 36 key: privateKey, 37 padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, 38 oaepHash: 'sha256', 39 }, 40 encrypted 41 ).toString(); 42} 43 44// Usage 45const encrypted = rsaEncrypt('Secret', publicKey); 46const decrypted = rsaDecrypt(encrypted, privateKey);

Digital Signatures#

1import crypto from 'node:crypto'; 2 3// Sign data 4function sign(data, privateKey) { 5 const signer = crypto.createSign('RSA-SHA256'); 6 signer.update(data); 7 return signer.sign(privateKey, 'hex'); 8} 9 10// Verify signature 11function verify(data, signature, publicKey) { 12 const verifier = crypto.createVerify('RSA-SHA256'); 13 verifier.update(data); 14 return verifier.verify(publicKey, signature, 'hex'); 15} 16 17// Usage 18const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { 19 modulusLength: 2048, 20}); 21 22const data = 'Data to sign'; 23const signature = sign(data, privateKey); 24const isValid = verify(data, signature, publicKey); 25console.log('Signature valid:', isValid); // true

Key Derivation#

1import crypto from 'node:crypto'; 2 3// PBKDF2 4function deriveKeyPbkdf2(password, salt, iterations = 100000) { 5 return new Promise((resolve, reject) => { 6 crypto.pbkdf2( 7 password, 8 salt, 9 iterations, 10 64, // key length 11 'sha512', 12 (err, derivedKey) => { 13 if (err) reject(err); 14 else resolve(derivedKey); 15 } 16 ); 17 }); 18} 19 20// Scrypt (more secure) 21function deriveKeyScrypt(password, salt) { 22 return new Promise((resolve, reject) => { 23 crypto.scrypt(password, salt, 64, (err, derivedKey) => { 24 if (err) reject(err); 25 else resolve(derivedKey); 26 }); 27 }); 28} 29 30// Usage 31const salt = crypto.randomBytes(16); 32const key = await deriveKeyPbkdf2('password', salt); 33console.log('Derived key:', key.toString('hex'));

Secure Token Generation#

1import crypto from 'node:crypto'; 2 3// API token 4function generateApiToken() { 5 return crypto.randomBytes(32).toString('base64url'); 6} 7 8// Session token 9function generateSessionToken() { 10 return crypto.randomBytes(48).toString('hex'); 11} 12 13// Reset token with expiry 14function generateResetToken() { 15 const token = crypto.randomBytes(32).toString('hex'); 16 const expires = Date.now() + 3600000; // 1 hour 17 const hash = crypto.createHash('sha256').update(token).digest('hex'); 18 19 return { 20 token, // Send to user 21 hash, // Store in database 22 expires, 23 }; 24} 25 26// Verify reset token 27function verifyResetToken(token, storedHash, expiry) { 28 if (Date.now() > expiry) return false; 29 const hash = crypto.createHash('sha256').update(token).digest('hex'); 30 return crypto.timingSafeEqual( 31 Buffer.from(hash), 32 Buffer.from(storedHash) 33 ); 34}

File Hashing#

1import crypto from 'node:crypto'; 2import fs from 'node:fs'; 3 4// Hash file using streams 5function hashFile(filePath, algorithm = 'sha256') { 6 return new Promise((resolve, reject) => { 7 const hash = crypto.createHash(algorithm); 8 const stream = fs.createReadStream(filePath); 9 10 stream.on('data', (data) => hash.update(data)); 11 stream.on('end', () => resolve(hash.digest('hex'))); 12 stream.on('error', reject); 13 }); 14} 15 16// Verify file integrity 17async function verifyFileIntegrity(filePath, expectedHash) { 18 const actualHash = await hashFile(filePath); 19 return crypto.timingSafeEqual( 20 Buffer.from(actualHash), 21 Buffer.from(expectedHash) 22 ); 23}

Best Practices#

Hashing: ✓ Use SHA-256 or SHA-512 ✓ Use bcrypt/scrypt for passwords ✓ Always use salt ✓ Use timingSafeEqual for comparison Encryption: ✓ Use AES-256-GCM for symmetric ✓ Use RSA-OAEP for asymmetric ✓ Generate random IVs ✓ Store keys securely Random: ✓ Use crypto.randomBytes ✓ Use crypto.randomUUID for UUIDs ✓ Use crypto.randomInt for numbers ✓ Never use Math.random for security Avoid: ✗ MD5 or SHA-1 for security ✗ Hardcoded keys/secrets ✗ ECB mode encryption ✗ Custom crypto implementations

Conclusion#

The Node.js crypto module provides comprehensive cryptographic functionality. Use modern algorithms like SHA-256 for hashing, AES-256-GCM for encryption, and scrypt for password hashing. Always use cryptographically secure random number generation and never roll your own crypto. Store keys securely and use appropriate key lengths for your security requirements.

Share this article

Help spread the word about Bootspring