Back to Blog
Node.jsCryptoSecurityBackend

Node.js Crypto Module Guide

Master Node.js cryptography for hashing, encryption, and secure operations.

B
Bootspring Team
Engineering
July 31, 2018
7 min read

The crypto module provides cryptographic functionality for hashing, encryption, and secure operations.

Hashing#

1import { createHash } from 'crypto'; 2 3// Basic hash 4const hash = createHash('sha256') 5 .update('Hello, World!') 6 .digest('hex'); 7console.log(hash); 8 9// Supported algorithms 10createHash('md5'); 11createHash('sha1'); 12createHash('sha256'); 13createHash('sha512'); 14 15// Multiple updates 16const multiHash = createHash('sha256'); 17multiHash.update('Hello, '); 18multiHash.update('World!'); 19const result = multiHash.digest('hex'); 20 21// Different output formats 22createHash('sha256').update('data').digest('hex'); // Hexadecimal 23createHash('sha256').update('data').digest('base64'); // Base64 24createHash('sha256').update('data').digest('binary'); // Binary string 25createHash('sha256').update('data').digest(); // Buffer 26 27// File hashing 28import { createReadStream } from 'fs'; 29 30async function hashFile(path) { 31 return new Promise((resolve, reject) => { 32 const hash = createHash('sha256'); 33 const stream = createReadStream(path); 34 35 stream.on('data', data => hash.update(data)); 36 stream.on('end', () => resolve(hash.digest('hex'))); 37 stream.on('error', reject); 38 }); 39}

HMAC#

1import { createHmac } from 'crypto'; 2 3// HMAC for message authentication 4const secret = 'my-secret-key'; 5const hmac = createHmac('sha256', secret) 6 .update('message to authenticate') 7 .digest('hex'); 8 9console.log(hmac); 10 11// Verify HMAC 12function verifyHmac(message, receivedHmac, secret) { 13 const computed = createHmac('sha256', secret) 14 .update(message) 15 .digest('hex'); 16 17 // Use timing-safe comparison 18 return timingSafeEqual( 19 Buffer.from(computed), 20 Buffer.from(receivedHmac) 21 ); 22} 23 24// Webhook signature verification 25function verifyWebhook(payload, signature, secret) { 26 const expected = createHmac('sha256', secret) 27 .update(payload) 28 .digest('hex'); 29 30 return `sha256=${expected}` === signature; 31}

Password Hashing#

1import { scrypt, randomBytes, timingSafeEqual } from 'crypto'; 2import { promisify } from 'util'; 3 4const scryptAsync = promisify(scrypt); 5 6// Hash password 7async function hashPassword(password) { 8 const salt = randomBytes(16).toString('hex'); 9 const derivedKey = await scryptAsync(password, salt, 64); 10 return `${salt}:${derivedKey.toString('hex')}`; 11} 12 13// Verify password 14async function verifyPassword(password, stored) { 15 const [salt, hash] = stored.split(':'); 16 const derivedKey = await scryptAsync(password, salt, 64); 17 const hashBuffer = Buffer.from(hash, 'hex'); 18 return timingSafeEqual(derivedKey, hashBuffer); 19} 20 21// Usage 22const hashed = await hashPassword('myPassword123'); 23console.log('Hashed:', hashed); 24 25const isValid = await verifyPassword('myPassword123', hashed); 26console.log('Valid:', isValid); // true 27 28// PBKDF2 alternative 29import { pbkdf2 } from 'crypto'; 30 31const pbkdf2Async = promisify(pbkdf2); 32 33async function hashWithPbkdf2(password) { 34 const salt = randomBytes(16); 35 const key = await pbkdf2Async(password, salt, 100000, 64, 'sha512'); 36 return `${salt.toString('hex')}:${key.toString('hex')}`; 37}

Symmetric Encryption#

1import { 2 createCipheriv, 3 createDecipheriv, 4 randomBytes, 5 scrypt 6} from 'crypto'; 7import { promisify } from 'util'; 8 9const scryptAsync = promisify(scrypt); 10 11// AES-256-GCM encryption (recommended) 12async function encrypt(text, password) { 13 const salt = randomBytes(16); 14 const key = await scryptAsync(password, salt, 32); 15 const iv = randomBytes(16); 16 17 const cipher = createCipheriv('aes-256-gcm', key, iv); 18 let encrypted = cipher.update(text, 'utf8', 'hex'); 19 encrypted += cipher.final('hex'); 20 21 const authTag = cipher.getAuthTag(); 22 23 return { 24 encrypted, 25 iv: iv.toString('hex'), 26 salt: salt.toString('hex'), 27 authTag: authTag.toString('hex') 28 }; 29} 30 31async function decrypt(encryptedData, password) { 32 const { encrypted, iv, salt, authTag } = encryptedData; 33 const key = await scryptAsync(password, Buffer.from(salt, 'hex'), 32); 34 35 const decipher = createDecipheriv( 36 'aes-256-gcm', 37 key, 38 Buffer.from(iv, 'hex') 39 ); 40 decipher.setAuthTag(Buffer.from(authTag, 'hex')); 41 42 let decrypted = decipher.update(encrypted, 'hex', 'utf8'); 43 decrypted += decipher.final('utf8'); 44 45 return decrypted; 46} 47 48// Usage 49const encrypted = await encrypt('Secret message', 'password123'); 50const decrypted = await decrypt(encrypted, 'password123'); 51console.log(decrypted); // 'Secret message'

Asymmetric Encryption#

1import { 2 generateKeyPairSync, 3 publicEncrypt, 4 privateDecrypt 5} from 'crypto'; 6 7// Generate key pair 8const { publicKey, privateKey } = generateKeyPairSync('rsa', { 9 modulusLength: 2048, 10 publicKeyEncoding: { type: 'spki', format: 'pem' }, 11 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } 12}); 13 14// Encrypt with public key 15const encrypted = publicEncrypt( 16 publicKey, 17 Buffer.from('Secret message') 18); 19 20console.log('Encrypted:', encrypted.toString('base64')); 21 22// Decrypt with private key 23const decrypted = privateDecrypt(privateKey, encrypted); 24console.log('Decrypted:', decrypted.toString()); 25 26// Generate key pair async 27import { generateKeyPair } from 'crypto'; 28 29generateKeyPair('rsa', { 30 modulusLength: 2048 31}, (err, publicKey, privateKey) => { 32 if (err) throw err; 33 // Use keys 34});

Digital Signatures#

1import { createSign, createVerify, generateKeyPairSync } from 'crypto'; 2 3// Generate keys for signing 4const { publicKey, privateKey } = generateKeyPairSync('rsa', { 5 modulusLength: 2048 6}); 7 8// Sign data 9function sign(data, privateKey) { 10 const signer = createSign('SHA256'); 11 signer.update(data); 12 signer.end(); 13 return signer.sign(privateKey, 'hex'); 14} 15 16// Verify signature 17function verify(data, signature, publicKey) { 18 const verifier = createVerify('SHA256'); 19 verifier.update(data); 20 verifier.end(); 21 return verifier.verify(publicKey, signature, 'hex'); 22} 23 24// Usage 25const data = 'Data to sign'; 26const signature = sign(data, privateKey); 27console.log('Signature:', signature); 28 29const isValid = verify(data, signature, publicKey); 30console.log('Valid signature:', isValid); // true 31 32// Ed25519 (faster, modern) 33const ed25519Keys = generateKeyPairSync('ed25519'); 34const ed25519Signature = sign(data, ed25519Keys.privateKey); 35const ed25519Valid = verify(data, ed25519Signature, ed25519Keys.publicKey);

Random Values#

1import { randomBytes, randomUUID, randomInt } from 'crypto'; 2 3// Random bytes 4const bytes = randomBytes(32); 5console.log('Random bytes:', bytes.toString('hex')); 6 7// Random UUID 8const uuid = randomUUID(); 9console.log('UUID:', uuid); 10 11// Random integer 12randomInt(100, (err, n) => { 13 console.log('Random 0-99:', n); 14}); 15 16// Synchronous random int 17const randomNum = randomInt(1, 100); // 1-99 18 19// Secure token generation 20function generateToken(length = 32) { 21 return randomBytes(length).toString('hex'); 22} 23 24// Generate random string 25function randomString(length, charset = 'alphanumeric') { 26 const charsets = { 27 alphanumeric: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 28 hex: '0123456789abcdef', 29 numeric: '0123456789' 30 }; 31 32 const chars = charsets[charset] || charset; 33 const bytes = randomBytes(length); 34 35 return Array.from(bytes) 36 .map(byte => chars[byte % chars.length]) 37 .join(''); 38} 39 40console.log(randomString(16));

Key Derivation#

1import { scrypt, pbkdf2, hkdf } from 'crypto'; 2import { promisify } from 'util'; 3 4// scrypt - memory-hard, good for passwords 5const scryptAsync = promisify(scrypt); 6 7async function deriveKeyScrypt(password, salt) { 8 const key = await scryptAsync(password, salt, 32, { 9 N: 16384, // CPU/memory cost 10 r: 8, // Block size 11 p: 1 // Parallelization 12 }); 13 return key; 14} 15 16// PBKDF2 - widely supported 17const pbkdf2Async = promisify(pbkdf2); 18 19async function deriveKeyPbkdf2(password, salt) { 20 const key = await pbkdf2Async( 21 password, 22 salt, 23 100000, // Iterations 24 32, // Key length 25 'sha512' // Digest 26 ); 27 return key; 28} 29 30// HKDF - for key expansion 31const hkdfAsync = promisify(hkdf); 32 33async function expandKey(key, salt, info, length = 32) { 34 const derived = await hkdfAsync('sha256', key, salt, info, length); 35 return derived; 36}

Diffie-Hellman Key Exchange#

1import { createDiffieHellman, createECDH } from 'crypto'; 2 3// Classic DH 4const alice = createDiffieHellman(2048); 5alice.generateKeys(); 6 7const bob = createDiffieHellman( 8 alice.getPrime(), 9 alice.getGenerator() 10); 11bob.generateKeys(); 12 13// Exchange public keys and compute shared secret 14const aliceSecret = alice.computeSecret(bob.getPublicKey()); 15const bobSecret = bob.computeSecret(alice.getPublicKey()); 16 17console.log('Secrets match:', aliceSecret.equals(bobSecret)); 18 19// ECDH (more efficient) 20const aliceECDH = createECDH('secp256k1'); 21aliceECDH.generateKeys(); 22 23const bobECDH = createECDH('secp256k1'); 24bobECDH.generateKeys(); 25 26const aliceShared = aliceECDH.computeSecret(bobECDH.getPublicKey()); 27const bobShared = bobECDH.computeSecret(aliceECDH.getPublicKey()); 28 29console.log('ECDH secrets match:', aliceShared.equals(bobShared));

Practical Examples#

1// API key generation 2function generateApiKey() { 3 const prefix = 'sk_live_'; 4 const key = randomBytes(24).toString('base64url'); 5 return prefix + key; 6} 7 8// Secure token for email verification 9function generateVerificationToken() { 10 return randomBytes(32).toString('hex'); 11} 12 13// Hash sensitive data for storage 14function hashSensitiveData(data) { 15 return createHash('sha256') 16 .update(data) 17 .digest('hex'); 18} 19 20// Checksum for file integrity 21async function calculateChecksum(filePath) { 22 return hashFile(filePath); // Using earlier hashFile function 23} 24 25// Secure cookie signing 26function signCookie(value, secret) { 27 const signature = createHmac('sha256', secret) 28 .update(value) 29 .digest('base64url'); 30 return `${value}.${signature}`; 31} 32 33function verifyCookie(signedValue, secret) { 34 const [value, signature] = signedValue.split('.'); 35 const expected = createHmac('sha256', secret) 36 .update(value) 37 .digest('base64url'); 38 39 if (timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { 40 return value; 41 } 42 return null; 43}

Best Practices#

Algorithms: ✓ SHA-256 or SHA-512 for hashing ✓ AES-256-GCM for encryption ✓ scrypt or Argon2 for passwords ✓ Ed25519 for signatures Security: ✓ Use timing-safe comparison ✓ Generate IVs/nonces randomly ✓ Store salts with hashes ✓ Use authenticated encryption Key Management: ✓ Generate strong random keys ✓ Rotate keys periodically ✓ Secure key storage ✓ Use key derivation Avoid: ✗ MD5 or SHA1 for security ✗ ECB mode encryption ✗ Hardcoded secrets ✗ Reusing IVs/nonces

Conclusion#

Node.js crypto module provides comprehensive cryptographic tools. Use appropriate algorithms for each task: SHA-256+ for hashing, AES-GCM for encryption, scrypt for passwords, and modern signature algorithms. Always use secure random generation, proper key derivation, and timing-safe comparisons. Keep security practices current as cryptographic recommendations evolve.

Share this article

Help spread the word about Bootspring