Cryptography in TypeScript: Encryption and Hashing — A Complete Guide
Modern web development is unthinkable without ensuring data security. TypeScript, as a superset of JavaScript, provides powerful tools for implementing cryptographic functions both on the client side and on the server (Node.js). In this article, we will thoroughly examine the main cryptographic primitives: symmetric and asymmetric encryption, hashing, and also look at working with digital signatures. All code examples are written in TypeScript and are ready for use in real projects.
1. Fundamentals of Cryptography in TypeScript
In the TypeScript ecosystem, there are two main approaches to cryptography:
- Web Crypto API — a built-in API for browsers and Node.js (starting from version 15), providing low-level access to cryptographic operations.
- Third-party libraries — such as
crypto-js,node-forge,bcryptjsfor specific tasks.
For server-side development on Node.js, it is recommended to use the built-in crypto module, which is part of the platform and does not require installing additional dependencies.
2. Data Hashing
Hashing is a one-way transformation of data into a fixed-length output. It is used for storing passwords, verifying file integrity, and creating checksums.
2.1. SHA-256 using the Web Crypto API
async function hashSHA256(data: string): Promise<string> { const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');}
// Example usageconst myHash = await hashSHA256('Hello, world!');console.log(myHash); // 64 hex characters2.2. Password Hashing with bcrypt
For secure password storage, use specialized algorithms with salt, such as bcrypt:
import bcrypt from 'bcryptjs';
async function hashPassword(password: string): Promise<string> { const salt = await bcrypt.genSalt(12); // 12 rounds — a good balance return await bcrypt.hash(password, salt);}
async function verifyPassword(password: string, hash: string): Promise<boolean> { return await bcrypt.compare(password, hash);}
// Exampleconst password = 'MySecurePass!';const hashedPassword = await hashPassword(password);const isValid = await verifyPassword('MySecurePass!', hashedPassword);console.log(isValid); // true3. Symmetric Encryption
Symmetric encryption uses a single key for both encrypting and decrypting data. The most modern and secure algorithm is AES-GCM, which provides both confidentiality and authentication.
3.1. AES-GCM Encryption and Decryption
interface EncryptedData { iv: Uint8Array; ciphertext: Uint8Array; tag: Uint8Array;}
async function encryptAESGCM( plaintext: string, key: CryptoKey): Promise<EncryptedData> { const encoder = new TextEncoder(); const data = encoder.encode(plaintext); const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, key, data ); // GCM appends a 16-byte authentication tag at the end const ciphertext = new Uint8Array(encrypted.slice(0, encrypted.byteLength - 16)); const tag = new Uint8Array(encrypted.slice(encrypted.byteLength - 16)); return { iv, ciphertext, tag };}
async function decryptAESGCM( encrypted: EncryptedData, key: CryptoKey): Promise<string> { // Combine ciphertext and tag for Web Cry