AES128 uses a 16 byte secret key.
const key = new Uint8Array(16);
crypto.getRandomValues(key);
const message = "hello";
// Asynchronous when using the Web Crypto API.
const encrypted = await encryptString(key, message);
const decrypted = await decryptToString(key, encrypted);
console.log(new TextDecoder().decode(decrypted));
Use hex or base64 encoding if you need to store the encrypted data as a string (base64 is more compact). For cookies, use base64url.
import { encodeBase64urlNoPadding } from "@oslojs/encoding";
const encrypted = await encryptString(key, message);
const encoded = encodeBase64urlNoPadding(encrypted);
You can also openssl
to generate the secret key in MacOS and Linux.
openssl rand --base64 16
On Windows, you can do this thing instead.
(1..16|%{[byte](Get-Random -Max 256)}|foreach ToString X2) -join ''
These will be base64-encoded so decode it before usage.
import { decodeBase64 } from "@oslojs/encoding";
const key = decodeBase64(process.env.ENCYRPTION_KEY);
// AES128 with the Web Crypto API.
async function encrypt(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {
const iv = new Uint8Array(16);
crypto.getRandomValues(iv);
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
const cipher = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
tagLength: 128
},
cryptoKey,
data
);
const encrypted = new Uint8Array(iv.byteLength + cipher.byteLength);
encrypted.set(iv);
encrypted.set(new Uint8Array(cipher), iv.byteLength);
return encrypted;
}
async function encryptString(key: Uint8Array, data: string): Uint8Array {
const encoded = new TextEncoder().encode(data));
const encrypted = await encrypt(encoded);
return encrypted;
}
async function decrypt(key: Uint8Array, encrypted: Uint8Array): Promise<Uint8Array> {
if (encrypted.length < 16) {
throw new Error("Invalid data");
}
const cryptoKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["decrypt"]);
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: encrypted.slice(0, 16),
tagLength: 128
},
cryptoKey,
encrypted.slice(16)
);
return new Uint8Array(decrypted);
}
async function decryptToString(key: Uint8Array, data: Uint8Array): string {
const decrypted = await decrypt(key, data);
const decoded = new TextDecoder().decode(decrypted);
return decoded;
}
import { createCipheriv, createDecipheriv } from "crypto";
import { DynamicBuffer } from "@oslojs/binary";
function encrypt(key: Uint8Array, data: Uint8Array): Uint8Array {
const iv = new Uint8Array(16);
crypto.getRandomValues(iv);
const cipher = createCipheriv("aes-128-gcm", key, iv);
const encrypted = new DynamicBuffer(0);
encrypted.write(iv);
encrypted.write(cipher.update(data));
encrypted.write(cipher.final());
encrypted.write(cipher.getAuthTag());
return encrypted.bytes();
}
function encryptString(key: Uint8Array, data: string): Uint8Array {
const encoded = new TextEncoder().encode(data));
const encrypted = encrypt(encoded);
return encrypted;
}
function decrypt(key: Uint8Array, encrypted: Uint8Array): Uint8Array {
if (encrypted.byteLength < 33) {
throw new Error("Invalid data");
}
const decipher = createDecipheriv("aes-128-gcm", key, encrypted.slice(0, 16));
decipher.setAuthTag(encrypted.slice(encrypted.byteLength - 16));
const decrypted = new DynamicBuffer(0);
decrypted.write(decipher.update(encrypted.slice(16, encrypted.byteLength - 16)));
decrypted.write(decipher.final());
return decrypted.bytes();
}
function decryptToString(key: Uint8Array, data: Uint8Array): string {
const decrypted = decrypt(key, data);
const decoded = new TextDecoder().decode(decrypted);
return decoded;
}