Created
November 17, 2023 09:45
-
-
Save imran-vz/2b7fb74c6223b2fce506ce3a4a7e6ad9 to your computer and use it in GitHub Desktop.
Encrypt and decrypt data on the browser using CryptoSubtle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* @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