The crypto module provides cryptographic functionality for hashing, encryption, and secure random numbers.
Hashing#
1const crypto = require('crypto');
2
3// Create hash
4function hash(data, algorithm = 'sha256') {
5 return crypto
6 .createHash(algorithm)
7 .update(data)
8 .digest('hex');
9}
10
11console.log(hash('hello world'));
12// a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
13
14// Different algorithms
15console.log(hash('hello', 'md5')); // 5d41402abc4b2a76b9719d911017c592
16console.log(hash('hello', 'sha1')); // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
17console.log(hash('hello', 'sha512')); // 9b71d224bd62f...
18
19// Hash file
20const fs = require('fs');
21
22function hashFile(filepath) {
23 return new Promise((resolve, reject) => {
24 const hash = crypto.createHash('sha256');
25 const stream = fs.createReadStream(filepath);
26
27 stream.on('data', data => hash.update(data));
28 stream.on('end', () => resolve(hash.digest('hex')));
29 stream.on('error', reject);
30 });
31}
32
33// Streaming hash
34async function hashLargeFile(filepath) {
35 const hash = crypto.createHash('sha256');
36
37 for await (const chunk of fs.createReadStream(filepath)) {
38 hash.update(chunk);
39 }
40
41 return hash.digest('hex');
42}Password Hashing#
1const crypto = require('crypto');
2
3// Using scrypt (recommended)
4function 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
15async function verifyPassword(password, storedHash) {
16 const [salt, hash] = storedHash.split(':');
17
18 return new Promise((resolve, reject) => {
19 crypto.scrypt(password, salt, 64, (err, derivedKey) => {
20 if (err) reject(err);
21 resolve(derivedKey.toString('hex') === hash);
22 });
23 });
24}
25
26// Usage
27const hashed = await hashPassword('myPassword123');
28console.log(hashed); // salt:hash
29
30const isValid = await verifyPassword('myPassword123', hashed);
31console.log(isValid); // true
32
33// Using pbkdf2
34function hashPasswordPbkdf2(password) {
35 return new Promise((resolve, reject) => {
36 const salt = crypto.randomBytes(16).toString('hex');
37
38 crypto.pbkdf2(password, salt, 100000, 64, 'sha512', (err, derivedKey) => {
39 if (err) reject(err);
40 resolve(`${salt}:${derivedKey.toString('hex')}`);
41 });
42 });
43}Symmetric Encryption#
1const crypto = require('crypto');
2
3// AES-256-GCM encryption
4function encrypt(text, key) {
5 const iv = crypto.randomBytes(16);
6 const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
7
8 let encrypted = cipher.update(text, 'utf8', 'hex');
9 encrypted += cipher.final('hex');
10
11 const authTag = cipher.getAuthTag();
12
13 return {
14 iv: iv.toString('hex'),
15 encrypted,
16 authTag: authTag.toString('hex'),
17 };
18}
19
20function decrypt(encryptedData, key) {
21 const decipher = crypto.createDecipheriv(
22 'aes-256-gcm',
23 key,
24 Buffer.from(encryptedData.iv, 'hex')
25 );
26
27 decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
28
29 let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
30 decrypted += decipher.final('utf8');
31
32 return decrypted;
33}
34
35// Usage
36const key = crypto.randomBytes(32); // 256 bits
37const encrypted = encrypt('secret message', key);
38const decrypted = decrypt(encrypted, key);
39console.log(decrypted); // 'secret message'
40
41// AES-256-CBC (simpler but no authentication)
42function encryptCBC(text, key) {
43 const iv = crypto.randomBytes(16);
44 const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
45
46 let encrypted = cipher.update(text, 'utf8', 'hex');
47 encrypted += cipher.final('hex');
48
49 return `${iv.toString('hex')}:${encrypted}`;
50}
51
52function decryptCBC(encryptedText, key) {
53 const [ivHex, encrypted] = encryptedText.split(':');
54 const iv = Buffer.from(ivHex, 'hex');
55 const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
56
57 let decrypted = decipher.update(encrypted, 'hex', 'utf8');
58 decrypted += decipher.final('utf8');
59
60 return decrypted;
61}HMAC#
1const crypto = require('crypto');
2
3// Create HMAC
4function createHmac(data, secret) {
5 return crypto
6 .createHmac('sha256', secret)
7 .update(data)
8 .digest('hex');
9}
10
11// Verify HMAC (timing-safe)
12function verifyHmac(data, secret, providedHmac) {
13 const calculated = createHmac(data, secret);
14 return crypto.timingSafeEqual(
15 Buffer.from(calculated, 'hex'),
16 Buffer.from(providedHmac, 'hex')
17 );
18}
19
20// Usage: API request signing
21function signRequest(method, path, body, secret) {
22 const timestamp = Date.now().toString();
23 const message = `${method}:${path}:${timestamp}:${JSON.stringify(body)}`;
24
25 return {
26 signature: createHmac(message, secret),
27 timestamp,
28 };
29}
30
31function verifyRequest(method, path, body, timestamp, signature, secret) {
32 const message = `${method}:${path}:${timestamp}:${JSON.stringify(body)}`;
33 return verifyHmac(message, secret, signature);
34}
35
36// Webhook verification
37function verifyWebhook(payload, signature, secret) {
38 const expectedSignature = createHmac(payload, secret);
39 return crypto.timingSafeEqual(
40 Buffer.from(signature),
41 Buffer.from(expectedSignature)
42 );
43}Random Values#
1const crypto = require('crypto');
2
3// Random bytes
4const randomBytes = crypto.randomBytes(32);
5console.log(randomBytes.toString('hex'));
6
7// Random hex string
8function randomHex(length) {
9 return crypto.randomBytes(length).toString('hex');
10}
11
12// Random base64 string
13function randomBase64(length) {
14 return crypto.randomBytes(length).toString('base64');
15}
16
17// Random URL-safe string
18function randomUrlSafe(length) {
19 return crypto
20 .randomBytes(length)
21 .toString('base64')
22 .replace(/\+/g, '-')
23 .replace(/\//g, '_')
24 .replace(/=/g, '');
25}
26
27// Random integer in range
28function randomInt(min, max) {
29 return crypto.randomInt(min, max);
30}
31
32// Secure token generation
33function generateToken(length = 32) {
34 return crypto.randomBytes(length).toString('hex');
35}
36
37// UUID v4 (use crypto.randomUUID in Node 14.17+)
38function generateUUID() {
39 if (crypto.randomUUID) {
40 return crypto.randomUUID();
41 }
42
43 const bytes = crypto.randomBytes(16);
44 bytes[6] = (bytes[6] & 0x0f) | 0x40;
45 bytes[8] = (bytes[8] & 0x3f) | 0x80;
46
47 const hex = bytes.toString('hex');
48 return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
49}Key Derivation#
1const crypto = require('crypto');
2
3// Derive key from password
4function deriveKey(password, salt, keyLength = 32) {
5 return new Promise((resolve, reject) => {
6 crypto.scrypt(password, salt, keyLength, (err, derivedKey) => {
7 if (err) reject(err);
8 resolve(derivedKey);
9 });
10 });
11}
12
13// HKDF (Node.js 15+)
14async function deriveKeyHKDF(secret, salt, info, keyLength = 32) {
15 const key = await crypto.subtle.importKey(
16 'raw',
17 Buffer.from(secret),
18 'HKDF',
19 false,
20 ['deriveBits']
21 );
22
23 const derived = await crypto.subtle.deriveBits(
24 {
25 name: 'HKDF',
26 hash: 'SHA-256',
27 salt: Buffer.from(salt),
28 info: Buffer.from(info),
29 },
30 key,
31 keyLength * 8
32 );
33
34 return Buffer.from(derived);
35}
36
37// Key stretching for encryption
38async function createEncryptionKey(password) {
39 const salt = crypto.randomBytes(16);
40 const key = await deriveKey(password, salt, 32);
41
42 return {
43 key,
44 salt: salt.toString('hex'),
45 };
46}Digital Signatures#
1const crypto = require('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
18// Sign data
19function sign(data, privateKey) {
20 const signer = crypto.createSign('RSA-SHA256');
21 signer.update(data);
22 return signer.sign(privateKey, 'hex');
23}
24
25// Verify signature
26function verify(data, signature, publicKey) {
27 const verifier = crypto.createVerify('RSA-SHA256');
28 verifier.update(data);
29 return verifier.verify(publicKey, signature, 'hex');
30}
31
32// Usage
33const { publicKey, privateKey } = generateKeyPair();
34const message = 'Important message';
35const signature = sign(message, privateKey);
36const isValid = verify(message, signature, publicKey);
37console.log(isValid); // true
38
39// Ed25519 (faster, shorter keys)
40function generateEd25519KeyPair() {
41 return crypto.generateKeyPairSync('ed25519', {
42 publicKeyEncoding: { type: 'spki', format: 'pem' },
43 privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
44 });
45}Practical Examples#
1const crypto = require('crypto');
2
3// Session token
4function createSessionToken(userId) {
5 const payload = JSON.stringify({
6 userId,
7 created: Date.now(),
8 });
9
10 const token = crypto.randomBytes(32).toString('hex');
11 const hash = crypto
12 .createHash('sha256')
13 .update(payload + token)
14 .digest('hex');
15
16 return `${Buffer.from(payload).toString('base64')}.${hash}`;
17}
18
19// Password reset token
20function createResetToken() {
21 const token = crypto.randomBytes(32).toString('hex');
22 const hash = crypto.createHash('sha256').update(token).digest('hex');
23
24 return {
25 token, // Send to user
26 hash, // Store in database
27 expires: Date.now() + 3600000, // 1 hour
28 };
29}
30
31// Checksum for file integrity
32async function createChecksum(filepath) {
33 const hash = crypto.createHash('sha256');
34
35 for await (const chunk of require('fs').createReadStream(filepath)) {
36 hash.update(chunk);
37 }
38
39 return hash.digest('hex');
40}
41
42// Encrypt configuration
43function encryptConfig(config, masterKey) {
44 const key = crypto.scryptSync(masterKey, 'salt', 32);
45 const iv = crypto.randomBytes(16);
46 const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
47
48 const encrypted = Buffer.concat([
49 cipher.update(JSON.stringify(config), 'utf8'),
50 cipher.final(),
51 ]);
52
53 return {
54 encrypted: encrypted.toString('hex'),
55 iv: iv.toString('hex'),
56 authTag: cipher.getAuthTag().toString('hex'),
57 };
58}Best Practices#
Algorithms:
✓ Use scrypt or Argon2 for passwords
✓ Use AES-256-GCM for encryption
✓ Use SHA-256 or SHA-512 for hashing
✓ Use HMAC for message authentication
Security:
✓ Use crypto.randomBytes for tokens
✓ Use timing-safe comparison
✓ Generate unique IVs/salts
✓ Store salts with hashes
Key Management:
✓ Use environment variables
✓ Rotate keys periodically
✓ Use key derivation functions
✓ Never hardcode secrets
Avoid:
✗ MD5 or SHA1 for security
✗ ECB mode for encryption
✗ Predictable IVs or salts
✗ Rolling your own crypto
Conclusion#
The Node.js crypto module provides comprehensive cryptographic functionality. Use scrypt for password hashing, AES-GCM for encryption, and HMAC for authentication. Always use secure random generation for tokens and keys. Follow best practices for algorithm selection and key management.