In today’s digital age, securing sensitive information like API keys, passwords, and user data is more critical than ever. A robust encryption and decryption strategy can prevent unauthorized access and ensure data confidentiality. In this blog post, we’ll explore how to encrypt and decrypt text using vanilla JavaScript, leveraging the Web Crypto API for a modern, secure approach.
Why Use Encryption?
Encryption transforms readable data (plaintext) into a scrambled format (ciphertext) that can only be read if decrypted with the correct key. This ensures that even if someone intercepts the encrypted data, it remains meaningless without the key. A solid encryption mechanism protects:
- API keys stored in your frontend code.
- Sensitive user information.
- Any data transferred over insecure channels.
Let’s dive into how you can implement this securely in JavaScript.
Encryption and Decryption Using AES-GCM
We’ll use AES-GCM (Advanced Encryption Standard - Galois/Counter Mode), a modern standard that provides both encryption and integrity verification. The steps involve:
- Password Derivation: Use PBKDF2 (Password-Based Key Derivation Function 2) to derive a secure key from a password.
-
Salt and IV: Generate a random
salt
(to make the password derivation unique) andiv
(Initialization Vector) for each encryption. - Encryption: Encrypt the plaintext using AES-GCM.
-
Decryption: Decrypt the ciphertext using the same password and
salt
/iv
.
Code Implementation
Here is the complete JavaScript implementation.
Utilities for Conversion
We’ll convert between ArrayBuffer
and hexadecimal for easy data storage and retrieval:
function arrayBufferToHex(buffer) {
return [...new Uint8Array(buffer)]
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
function hexToArrayBuffer(hex) {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
}
return bytes.buffer;
}
Key Derivation from Password
Use PBKDF2 to derive a strong encryption key:
async function getCryptoKey(password) {
const encoder = new TextEncoder();
const keyMaterial = encoder.encode(password);
return crypto.subtle.importKey(
'raw',
keyMaterial,
{ name: 'PBKDF2' },
false,
['deriveKey']
);
}
async function deriveKey(password, salt) {
const keyMaterial = await getCryptoKey(password);
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
}
Encryption Function
Encrypt text with a password:
async function encryptText(text, password) {
const encoder = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKey(password, salt);
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
key,
encoder.encode(text)
);
return {
cipherText: arrayBufferToHex(encrypted),
iv: arrayBufferToHex(iv),
salt: arrayBufferToHex(salt)
};
}
Decryption Function
Decrypt text with the same password:
async function decryptText(encryptedData, password) {
const { cipherText, iv, salt } = encryptedData;
const key = await deriveKey(password, hexToArrayBuffer(salt));
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: hexToArrayBuffer(iv) },
key,
hexToArrayBuffer(cipherText)
);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
Example Usage
Let’s see how to use these functions:
(async () => {
const text = 'MySecretAPIKey123456';
const password = 'StrongPassword123!';
// Encrypt
const encryptedData = await encryptText(text, password);
console.log('Encrypted Data:', encryptedData);
// Decrypt
const decryptedText = await decryptText(encryptedData, password);
console.log('Decrypted Text:', decryptedText);
})();
Security Best Practices
- Use a Strong Password: The encryption is only as secure as the password you use.
-
Store Salt and IV Safely: Always save the
salt
andiv
alongside your encrypted data. - Avoid Hardcoding Secrets: Never hardcode sensitive data or passwords in your codebase.
- Use HTTPS: Ensure your application uses HTTPS to protect data in transit.
Encrypting sensitive information like API keys is a fundamental step in securing your applications. I use this for API keys mostly.
Top comments (0)