Text encryption is essential for protecting sensitive information in modern applications. This comprehensive guide covers AES-256 encryption using the Web Crypto API, best practices for secure encryption/decryption, and implementation patterns for browser-based cryptography.
Text encryption transforms readable plaintext into unreadable ciphertext using cryptographic algorithms and keys. Proper encryption ensures that only authorized parties with the correct key can decrypt and read the original message.
AES (Advanced Encryption Standard) is a symmetric encryption algorithm adopted by the U.S. government in 2001. AES-256 uses a 256-bit key, providing extremely strong security against brute-force attacks.
async function encryptText(plaintext, password) {
// Convert password to key
const enc = new TextEncoder();
const passwordBuffer = enc.encode(password);
// Import password as key material
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
passwordBuffer,
'PBKDF2',
false,
['deriveBits', 'deriveKey']
);
// Generate salt
const salt = window.crypto.getRandomValues(new Uint8Array(16));
// Derive AES key from password
const key = await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
// Generate IV
const iv = window.crypto.getRandomValues(new Uint8Array(12));
// Encrypt
const plaintextBuffer = enc.encode(plaintext);
const ciphertext = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv
},
key,
plaintextBuffer
);
// Combine salt + IV + ciphertext
const result = new Uint8Array(
salt.length + iv.length + ciphertext.byteLength
);
result.set(salt, 0);
result.set(iv, salt.length);
result.set(new Uint8Array(ciphertext), salt.length + iv.length);
// Convert to base64
return btoa(String.fromCharCode(...result));
}
// Usage
const encrypted = await encryptText('Secret message', 'my-password');
console.log(encrypted);
async function decryptText(encryptedData, password) {
// Decode base64
const data = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
// Extract salt, IV, and ciphertext
const salt = data.slice(0, 16);
const iv = data.slice(16, 28);
const ciphertext = data.slice(28);
// Convert password to key
const enc = new TextEncoder();
const passwordBuffer = enc.encode(password);
// Import password as key material
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
passwordBuffer,
'PBKDF2',
false,
['deriveBits', 'deriveKey']
);
// Derive same AES key
const key = await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
// Decrypt
const plaintext = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv
},
key,
ciphertext
);
// Convert to string
const dec = new TextDecoder();
return dec.decode(plaintext);
}
// Usage
const decrypted = await decryptText(encrypted, 'my-password');
console.log(decrypted); // "Secret message"
// Galois/Counter Mode - Authenticated encryption
const config = {
name: 'AES-GCM',
iv: iv, // 12 bytes
tagLength: 128 // Authentication tag length
};
// Provides:
// - Confidentiality (encryption)
// - Authenticity (detects tampering)
// - Faster than CBC + HMAC
// Cipher Block Chaining - Requires HMAC for authentication
const config = {
name: 'AES-CBC',
iv: iv // 16 bytes
};
// Note: Use with HMAC for authenticated encryption
// GCM is preferred for modern applications
async function deriveKey(password, salt, iterations = 100000) {
const enc = new TextEncoder();
const passwordBuffer = enc.encode(password);
// Import password
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
passwordBuffer,
'PBKDF2',
false,
['deriveKey']
);
// Derive AES key
return await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: iterations, // Higher = more secure but slower
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
true, // extractable
['encrypt', 'decrypt']
);
}
// Recommended iterations:
// - 100,000+ for production
// - 600,000+ for high security
// - 1,000,000+ for maximum security (slower)
async function encryptFile(file, password) {
// Read file
const fileBuffer = await file.arrayBuffer();
// Generate key
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const key = await deriveKey(password, salt);
// Generate IV
const iv = window.crypto.getRandomValues(new Uint8Array(12));
// Encrypt
const ciphertext = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
key,
fileBuffer
);
// Create encrypted file
const result = new Blob([salt, iv, ciphertext], {
type: 'application/octet-stream'
});
return result;
}
// Usage
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const encrypted = await encryptFile(file, 'password123');
// Download encrypted file
const url = URL.createObjectURL(encrypted);
const a = document.createElement('a');
a.href = url;
a.download = `${file.name}.encrypted`;
a.click();
// Bad passwords
'password'
'123456'
'admin'
// Good passwords (20+ characters recommended)
'correct-horse-battery-staple-2024'
'My$ecure!P@ssw0rd#2024'
'Th1s!sA$tr0ng&L0ngP@ssw0rd'
// Password strength check
function checkPasswordStrength(password) {
const minLength = 12;
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
const strength = [
password.length >= minLength,
hasUpper,
hasLower,
hasNumber,
hasSpecial
].filter(Boolean).length;
return {
score: strength,
verdict: strength >= 4 ? 'Strong' :
strength >= 3 ? 'Medium' : 'Weak'
};
}
// Good - Cryptographically secure
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const salt = window.crypto.getRandomValues(new Uint8Array(16));
// Bad - NOT cryptographically secure
const badIV = Math.random(); // Never use Math.random() for crypto!
// Always generate new IV for each encryption
async function encrypt(data, key) {
const iv = window.crypto.getRandomValues(new Uint8Array(12));
// ... encryption code
}
// Never do this:
const STATIC_IV = new Uint8Array(12); // WRONG! Security vulnerability
// Correct storage format:
// [salt (16 bytes)][IV (12 bytes)][ciphertext (variable)]
// Salt and IV can be public - they're not secret
// Only the key/password must remain secret
async function secureDecrypt(encrypted, password) {
try {
return await decryptText(encrypted, password);
} catch (error) {
// Don't reveal specific error details
throw new Error('Decryption failed. Invalid password or corrupted data.');
}
}
async function saveEncryptedNote(content, password) {
const encrypted = await encryptText(content, password);
localStorage.setItem('encrypted_note', encrypted);
}
async function loadEncryptedNote(password) {
const encrypted = localStorage.getItem('encrypted_note');
if (!encrypted) return null;
return await decryptText(encrypted, password);
}
async function storeAPIKey(apiKey, masterPassword) {
const encrypted = await encryptText(apiKey, masterPassword);
localStorage.setItem('api_key_encrypted', encrypted);
}
async function retrieveAPIKey(masterPassword) {
const encrypted = localStorage.getItem('api_key_encrypted');
return await decryptText(encrypted, masterPassword);
}
async function sendEncryptedData(data, recipientPublicKey) {
// Encrypt with shared secret
const encrypted = await encryptText(
JSON.stringify(data),
sharedSecret
);
// Send encrypted data
await fetch('/api/secure-endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ encrypted })
});
}
// Test encryption/decryption round trip
async function testEncryption() {
const original = 'Test message 123!@#';
const password = 'test-password';
const encrypted = await encryptText(original, password);
const decrypted = await decryptText(encrypted, password);
console.assert(
decrypted === original,
'Decryption failed to match original'
);
// Test wrong password
try {
await decryptText(encrypted, 'wrong-password');
console.assert(false, 'Should have thrown error');
} catch (e) {
console.log('Correctly rejected wrong password');
}
}
testEncryption();
// For large files, process in chunks
async function encryptLargeFile(file, password, chunkSize = 1024 * 1024) {
const chunks = [];
let offset = 0;
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize);
const encrypted = await encryptFile(chunk, password);
chunks.push(encrypted);
offset += chunkSize;
// Report progress
console.log(`Progress: ${Math.round(offset / file.size * 100)}%`);
}
return new Blob(chunks);
}
// Check Web Crypto API support
if (!window.crypto || !window.crypto.subtle) {
alert('Your browser does not support Web Crypto API');
} else {
// Safe to use encryption
console.log('Web Crypto API supported');
}
// Feature detection
async function checkCryptoSupport() {
const features = {
subtle: !!window.crypto?.subtle,
aesGcm: false,
pbkdf2: false
};
try {
// Test AES-GCM
const key = await window.crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
features.aesGcm = true;
// Test PBKDF2
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
new Uint8Array(16),
'PBKDF2',
false,
['deriveKey']
);
features.pbkdf2 = true;
} catch (e) {
console.error('Crypto feature not supported:', e);
}
return features;
}
Text encryption using AES-256 and the Web Crypto API provides robust security for sensitive data in browser-based applications. By following best practices for key generation, IV management, and password handling, you can implement secure encryption that protects user data effectively.
Use QuickUtil.dev's Text Encryption tool to quickly encrypt and decrypt text using AES-256 with a simple interface. All encryption happens client-side, ensuring your data never leaves your browser.
AES-256 (Advanced Encryption Standard with 256-bit key) is a symmetric encryption algorithm considered highly secure. It's used by governments and organizations worldwide for protecting classified and sensitive data.
Use the Web Crypto API's SubtleCrypto interface. Generate a key, create an initialization vector (IV), and use crypto.subtle.encrypt() with AES-GCM mode for authenticated encryption with additional data protection.
Yes, Web Crypto API is secure and designed for cryptographic operations in browsers. It uses native implementations, keeps keys in secure memory, and prevents extraction of raw key material.
AES-GCM provides authenticated encryption (confidentiality + integrity) and is faster. AES-CBC only provides confidentiality and requires separate authentication (HMAC). Use AES-GCM for modern applications.
No, AES-256 encrypted data cannot be practically decrypted without the correct key. Brute-forcing 256-bit AES would take billions of years with current technology.
Use at least 12-16 characters with mixed case, numbers, and symbols. Longer passwords (20+ characters) are better. Use passphrases for memorability while maintaining security.
An IV is a random value used with encryption algorithms to ensure identical plaintexts encrypt to different ciphertexts. It must be unique for each encryption operation but doesn't need to be secret.
Client-side encryption protects data before transmission, ensuring server never sees plaintext. Server-side handles key management better. Use client-side for end-to-end encryption, server-side for data at rest.
Protect sensitive information with AES-256 encryption. All encryption happens in your browser - your data never leaves your device.
Try the Text Encryption Tool Now