Skip to content

Instantly share code, notes, and snippets.

@imran-vz
Created November 17, 2023 09:45
Show Gist options
  • Save imran-vz/2b7fb74c6223b2fce506ce3a4a7e6ad9 to your computer and use it in GitHub Desktop.
Save imran-vz/2b7fb74c6223b2fce506ce3a4a7e6ad9 to your computer and use it in GitHub Desktop.
Encrypt and decrypt data on the browser using CryptoSubtle
/**
*
* @param {string} message
* @returns
*/
function getMessageEncoding(message) {
const enc = new TextEncoder();
return enc.encode(message);
}
/**
*
* @param {string} message
* @param {string} key
* @returns
*/
async function encryptMessage(message, key) {
const encoded = getMessageEncoding(message);
const iv = window.crypto.getRandomValues(new Uint8Array(16));
const cipherText = await window.crypto.subtle.encrypt({ name: "AES-CBC", iv }, key, encoded);
const cipherBuffer = arrayBufferToBase64(cipherText);
const ivBuffer = arrayBufferToBase64(iv);
return [ivBuffer, cipherBuffer].join("%%");
}
/**
*
* @param {string} message
* @param {string} key
* @returns
*/
async function decryptMessage(message, key) {
console.log(`⚓️ | message:`, message);
const [iv, cipherText] = message.split("%%");
let decrypted = await window.crypto.subtle.decrypt(
{ name: "AES-CBC", iv: base64ToArrayBuffer(iv) },
key,
base64ToArrayBuffer(cipherText),
);
let dec = new TextDecoder();
return dec.decode(decrypted);
}
/**
*
* @param {string} base64
* @returns
*/
function base64ToArrayBuffer(base64) {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function arrayBufferToBase64(arrayBuffer) {
return btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
}
window.crypto.subtle.generateKey({ name: "AES-CBC", length: 256 }, true, ["encrypt", "decrypt"]).then(async (key) => {
const message = await encryptMessage("window.crypto.subtle.generateKey", key);
const exportedKey = btoa(
JSON.stringify({
rawkey: arrayBufferToBase64(await window.crypto.subtle.exportKey("raw", key)),
algo: "AES-CBC",
kind: ["encrypt", "decrypt"],
}),
);
localStorage.setItem("key", exportedKey);
const localKey = localStorage.getItem("key");
if (!localKey) throw new Error("No key in localstorage");
const { rawkey, algo, kind } = JSON.parse(atob(localKey));
const importedKey = await window.crypto.subtle.importKey("raw", base64ToArrayBuffer(rawkey), algo, true, kind);
console.log(await decryptMessage(message, importedKey));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment