🔥Limited Offer: Get 50% OFFon AI & Full Stack Courses🔥
Back to Node.js Notes
Topic #30

Crypto Module


What is the Crypto Module?

The Crypto module is a built-in Node.js module that provides cryptographic functionality including:

  • Hash functions (SHA-256, SHA-512, etc.)
  • HMAC (Hash-based Message Authentication Code)
  • Symmetric encryption (AES, DES, etc.)
  • Asymmetric encryption (RSA, ECDSA, etc.)
  • Digital signatures and verification
  • Secure random number generation

The Crypto module is essential for applications that need to handle sensitive information securely.

The Crypto module wraps the OpenSSL library, providing access to well-established and tested cryptographic algorithms.

This module is often used to handle sensitive data, such as:

  • User authentication and password storage
  • Secure data transmission
  • File encryption and decryption
  • Secure communication channels

Getting Started with Crypto

Here's a quick example of using the Crypto module to hash a string:

Basic Hashing Example

const crypto = require('crypto');

// Create a SHA-256 hash of a string
const hash = crypto.createHash('sha256')
  .update('Hello, Node.js!')
  .digest('hex');
console.log('SHA-256 Hash:', hash);

Installing the Crypto Module

The Crypto module is included in Node.js by default.

You can use it by requiring it in your script:

const crypto = require('crypto');

Hash Functions

Hashing is a one-way transformation of data into a fixed-length string of characters.

Hash functions have several important properties:

  • Deterministic: Same input always produces the same output
  • Fixed Length: Output is always the same size regardless of input size
  • One-Way: Extremely difficult to reverse the process
  • Avalanche Effect: Small changes in input produce significant changes in output

Common use cases include:

  • Password storage
  • Data integrity verification
  • Digital signatures
  • Content addressing (e.g., Git, IPFS)

Creating a Hash

const crypto = require('crypto');

// Create a hash object
const hash = crypto.createHash('sha256');

// Update the hash with data
hash.update('Hello, World!');

// Get the digest in hexadecimal format
const digest = hash.digest('hex');
console.log(digest);

In this example:

  • createHash() creates a hash object with the specified algorithm
  • update() updates the hash content with the given data
  • digest() calculates the digest and outputs it in the specified format

Common Hash Algorithms

const crypto = require('crypto');
const data = 'Hello, World!';

// MD5 (not recommended for security-critical applications)
const md5 = crypto.createHash('md5').update(data).digest('hex');
console.log('MD5:', md5);

// SHA-1 (not recommended for security-critical applications)
const sha1 = crypto.createHash('sha1').update(data).digest('hex');
console.log('SHA-1:', sha1);

// SHA-256
const sha256 = crypto.createHash('sha256').update(data).digest('hex');
console.log('SHA-256:', sha256);

// SHA-512
const sha512 = crypto.createHash('sha512').update(data).digest('hex');
console.log('SHA-512:', sha512);

Warning: MD5 and SHA-1 are considered cryptographically weak and should not be used for security-critical applications. Use SHA-256, SHA-384, or SHA-512 instead.


Password Security

When handling passwords, it's crucial to use specialized password hashing functions that are designed to be computationally expensive to prevent brute-force attacks.

Here's why simple hashes are insufficient:

Warning: Never store passwords in plain text or with simple hashes like MD5 or SHA-1. These can be easily cracked using rainbow tables or brute-force attacks.

Key Concepts for Password Security

  1. Salting: Add a unique random value to each password before hashing
  2. Key Stretching: Make the hashing process intentionally slow to prevent brute-force attacks
  3. Work Factor: Control how computationally intensive the hashing process is

Here's how to properly hash passwords in Node.js:

Note: What is a salt? A salt is a random string that is unique to each user. It's combined with the password before hashing to ensure that even if two users have the same password, their hashes will be different. This prevents attackers from using precomputed tables (like rainbow tables) to crack multiple passwords at once.

const crypto = require('crypto');

// Function to hash a password
function hashPassword(password) {
  // Generate a random salt (16 bytes)
  const salt = crypto.randomBytes(16).toString('hex');

  // Use scrypt for password hashing (recommended)
  const hash = crypto.scryptSync(password, salt, 64).toString('hex');

  // Return both salt and hash for storage
  return { salt, hash };
}

// Function to verify a password
function verifyPassword(password, salt, hash) {
  const hashedPassword = crypto.scryptSync(password, salt, 64).toString('hex');
  return hashedPassword === hash;
}

// Example usage
const password = 'mySecurePassword';

// Hash the password for storage
const { salt, hash } = hashPassword(password);
console.log('Salt:', salt);
console.log('Hash:', hash);

// Verify a login attempt
const isValid = verifyPassword(password, salt, hash);
console.log('Password valid:', isValid); // true

const isInvalid = verifyPassword('wrongPassword', salt, hash);
console.log('Wrong password valid:', isInvalid); // false

Note: For password hashing in a production environment, consider using a dedicated library like bcrypt or argon2 that is specifically designed for secure password handling.


HMAC (Hash-based Message Authentication Code)

HMAC is a specific type of message authentication code (MAC) involving a cryptographic hash function and a secret cryptographic key.

It provides both data integrity and authentication.

When to Use HMAC

  • API request verification
  • Secure cookies and sessions
  • Data integrity checks
  • Webhook verification

HMAC Security Properties

  • Message Integrity: Any change to the message will produce a different HMAC
  • Authenticity: Only parties with the secret key can generate valid HMACs
  • No Encryption: HMAC doesn't encrypt the message, only verifies its integrity
const crypto = require('crypto');

// Secret key
const secretKey = 'mySecretKey';

// Create an HMAC
const hmac = crypto.createHmac('sha256', secretKey);

// Update with data
hmac.update('Hello, World!');

// Get the digest
const hmacDigest = hmac.digest('hex');
console.log('HMAC:', hmacDigest);

HMAC for Message Verification

const crypto = require('crypto');

// Function to create an HMAC for a message
function createSignature(message, key) {
  const hmac = crypto.createHmac('sha256', key);
  hmac.update(message);
  return hmac.digest('hex');
}

// Function to verify a message's signature
function verifySignature(message, signature, key) {
  const expectedSignature = createSignature(message, key);
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

// Example usage
const secretKey = 'verySecretKey';
const message = 'Important message to verify';

// Sender creates a signature
const signature = createSignature(message, secretKey);
console.log('Message:', message);
console.log('Signature:', signature);

// Receiver verifies the signature
try {
  const isValid = verifySignature(message, signature, secretKey);
  console.log('Signature valid:', isValid); // true

  // Try with a tampered message
  const isInvalid = verifySignature('Tampered message', signature, secretKey);
  console.log('Tampered message valid:', isInvalid); // false
} catch (error) {
  console.error('Verification error:', error.message);
}

Note: Always use timingSafeEqual() for cryptographic comparisons to prevent timing attacks.


Symmetric Encryption

Symmetric encryption uses the same key for both encryption and decryption.

It's generally faster than asymmetric encryption and is ideal for:

  • Bulk data encryption
  • Database encryption
  • Filesystem encryption
  • Secure messaging (combined with key exchange)

Common Symmetric Algorithms

Algorithm Key Size Block Size Notes
AES-256 256 bits 128 bits Current standard, widely used
ChaCha20 256 bits 512 bits Faster in software, used in TLS 1.3
3DES 168 bits 64 bits Legacy, not recommended for new systems
Blowfish 32-448 bits 64 bits Legacy, use Twofish or AES instead

Note: Always use authenticated encryption modes like AES-GCM or AES-CCM when possible, as they provide both confidentiality and authenticity.


AES (Advanced Encryption Standard)

const crypto = require('crypto');

// Function to encrypt data
function encrypt(text, key) {
  // Generate a random initialization vector
  const iv = crypto.randomBytes(16);

  // Create cipher with AES-256-CBC
  const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);

  // Encrypt the data
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');

  // Return both the encrypted data and the IV
  return {
    iv: iv.toString('hex'),
    encryptedData: encrypted
  };
}

// Function to decrypt data
function decrypt(encryptedData, iv, key) {
  // Create decipher
  const decipher = crypto.createDecipheriv(
    'aes-256-cbc',
    key,
    Buffer.from(iv, 'hex')
  );

  // Decrypt the data
  let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
  decrypted += decipher.final('utf8');

  return decrypted;
}

// Example usage
// Note: In a real application, use a properly generated and securely stored key
const key = crypto.scryptSync('secretPassword', 'salt', 32); // 32 bytes = 256 bits
const message = 'This is a secret message';

// Encrypt
const { iv, encryptedData } = encrypt(message, key);
console.log('Original:', message);
console.log('Encrypted:', encryptedData);
console.log('IV:', iv);

// Decrypt
const decrypted = decrypt(encryptedData, iv, key);
console.log('Decrypted:', decrypted);

Warning: Never reuse the same initialization vector (IV) with the same key. Always generate a new random IV for each encryption operation.


Other Symmetric Algorithms

The Crypto module supports various symmetric encryption algorithms.

You can see the available ciphers with:

const crypto = require('crypto');

// List available cipher algorithms
console.log(crypto.getCiphers());

Asymmetric Encryption

Asymmetric encryption (public-key cryptography) uses a pair of mathematically related keys:

  • Public Key: Can be shared publicly, used for encryption
  • Private Key: Must be kept secret, used for decryption

Common Use Cases

  • Secure key exchange (e.g., TLS/SSL handshake)
  • Digital signatures
  • Email encryption (PGP/GPG)
  • Blockchain and cryptocurrencies

Common Asymmetric Algorithms

Algorithm Key Size Security Level Notes
RSA 2048+ bits High Widely used, good compatibility
ECDSA 256-521 bits High Used in TLS 1.3, Bitcoin
Ed25519 256 bits Very High Modern, efficient, used in SSH

Warning: Performance Note: Asymmetric encryption is much slower than symmetric encryption. For encrypting large amounts of data, use a hybrid approach: Generate a random symmetric key Encrypt your data with the symmetric key Encrypt the symmetric key with the recipient's public key Send both the encrypted data and encrypted key


RSA (Rivest-Shamir-Adleman)

const crypto = require('crypto');

// Generate RSA key pair
function generateKeyPair() {
  return crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048, // Key size in bits
    publicKeyEncoding: {
      type: 'spki',
      format: 'pem'
    },
    privateKeyEncoding: {
      type: 'pkcs8',
      format: 'pem'
    }
  });
}

// Encrypt with public key
function encryptWithPublicKey(text, publicKey) {
  const buffer = Buffer.from(text, 'utf8');
  const encrypted = crypto.publicEncrypt(
    {
      key: publicKey,
      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
    },
    buffer
  );
  return encrypted.toString('base64');
}

// Decrypt with private key
function decryptWithPrivateKey(encryptedText, privateKey) {
  const buffer = Buffer.from(encryptedText, 'base64');
  const decrypted = crypto.privateDecrypt(
    {
      key: privateKey,
      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
    },
    buffer
  );
  return decrypted.toString('utf8');
}

// Generate keys
const { publicKey, privateKey } = generateKeyPair();
console.log('Public Key:', publicKey.substring(0, 50) + '...');
console.log('Private Key:', privateKey.substring(0, 50) + '...');

// Example usage
const message = 'This message is encrypted with RSA';
const encrypted = encryptWithPublicKey(message, publicKey);
console.log('Encrypted:', encrypted.substring(0, 50) + '...');

const decrypted = decryptWithPrivateKey(encrypted, privateKey);
console.log('Decrypted:', decrypted);

Note: RSA is typically used for encrypting small amounts of data (like encryption keys) due to performance constraints. For larger data, use a hybrid approach: encrypt the data with a symmetric algorithm (like AES) and encrypt the symmetric key with RSA.


Digital Signatures

Digital signatures provide a way to verify the authenticity and integrity of messages, software, or digital documents.

const crypto = require('crypto');

// Generate RSA key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem'
  }
});

// Function to sign a message
function signMessage(message, privateKey) {
  const signer = crypto.createSign('sha256');
  signer.update(message);
  return signer.sign(privateKey, 'base64');
}

// Function to verify a signature
function verifySignature(message, signature, publicKey) {
  const verifier = crypto.createVerify('sha256');
  verifier.update(message);
  return verifier.verify(publicKey, signature, 'base64');
}

// Example usage
const message = 'This message needs to be signed';
const signature = signMessage(message, privateKey);
console.log('Message:', message);
console.log('Signature:', signature.substring(0, 50) + '...');

// Verify the signature
const isValid = verifySignature(message, signature, publicKey);
console.log('Signature valid:', isValid); // true

// Verify with a modified message
const isInvalid = verifySignature('Modified message', signature, publicKey);
console.log('Modified message valid:', isInvalid); // false

Random Data Generation

Generating secure random data is important for many cryptographic operations, such as creating keys, salts, and initialization vectors.

const crypto = require('crypto');

// Generate random bytes
const randomBytes = crypto.randomBytes(16);
console.log('Random bytes:', randomBytes.toString('hex'));

// Generate a random string (Base64)
const randomString = crypto.randomBytes(32).toString('base64');
console.log('Random string:', randomString);

// Generate a random number between 1 and 100
function secureRandomNumber(min, max) {
  // Ensure we have enough randomness
  const range = max - min + 1;
  const bytesNeeded = Math.ceil(Math.log2(range) / 8);
  const maxValue = 256 ** bytesNeeded;

  // Generate random bytes and convert to a number
  const randomBytes = crypto.randomBytes(bytesNeeded);
  const randomValue = randomBytes.reduce((acc, byte, i) => {
    return acc + byte * (256 ** i);
  }, 0);

  // Scale to our range and shift by min
  return min + Math.floor((randomValue * range) / maxValue);
}

// Example: Generate 5 random numbers
for (let i = 0; i < 5; i++) {
  console.log(`Random number ${i+1}:`, secureRandomNumber(1, 100));
}

Security Best Practices

When using the Crypto module, keep these best practices in mind:

  • Use modern algorithms: Avoid MD5, SHA-1, and other outdated algorithms
  • Secure key management: Store keys securely, rotate them regularly, and never hardcode them
  • Use random IVs: Generate a new random IV for each encryption operation
  • Add authentication: Use authenticated encryption modes like GCM when possible
  • Constant-time comparisons: Always use crypto.timingSafeEqual() for comparing security-critical values
  • Key derivation: Use appropriate key derivation functions like scrypt, bcrypt, or PBKDF2 for password-based keys
  • Stay updated: Keep Node.js updated to get security fixes and support for newer algorithms
  • Follow standards: Adhere to established cryptographic standards and protocols

Warning: Cryptography is complex, and mistakes can lead to serious security vulnerabilities. When implementing critical security features, consider consulting a security specialist or using well-established libraries designed for specific cryptographic tasks.


Summary

The Node.js Crypto module provides a wide range of cryptographic functionality:

  • Hash functions for data integrity and fingerprinting
  • HMAC for authentication and integrity checks
  • Symmetric encryption for securing data with shared keys
  • Asymmetric encryption for secure communication and digital signatures
  • Random data generation for cryptographic operations
  • Digital signatures for authenticity verification

By understanding and properly implementing these cryptographic concepts, you can build secure applications that protect sensitive data and communications.

Want to go beyond the notes?

Join CodingNow 2.0's Node.js course — live mentorship, real projects, and 100% placement support.

Enroll Now — Free Demo Available

Crypto Module – FAQs

Quick answers about learning Crypto Module in Node.js.

This free note from CodingNow 2.0 explains Crypto Module in Node.js — concept, syntax and worked code examples you can copy, run and revise before interviews.
Yes. Every Node.js topic on CodingNow 2.0, including Crypto Module, is 100% free with no signup required.
With focused practice, most students grasp Crypto Module in 1–3 days from these notes; pairing it with CodingNow 2.0's mentor-led course takes you to job-ready depth faster.
Use the code examples in this note, then ask doubts for free on the CodingNow 2.0 Community (/community) — expert instructors answer within 24 hours.
WhatsApp
Call NowEnroll Now