Created
May 10, 2024 17:15
-
-
Save bradymholt/73a78c8fefab8481a9e9b8f160d53eb7 to your computer and use it in GitHub Desktop.
Web Crypto API Example
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
// Cryptography utilities that utilize the Web Crypto API | |
// By default, the AES-GCM algorithm is used for encryption and decryption. | |
const DEFAULT_ENCRYPTION_ALGORITHM = "AES-GCM"; | |
export function isCryptoSupported() { | |
return window.crypto && window.crypto.subtle && window.crypto.subtle.importKey; | |
} | |
async function buildEncryptionKey( | |
secretToken: string, | |
salt: Uint8Array, | |
keyUsages: KeyUsage[], | |
encryptionAlgorithm: string, | |
) { | |
const baseKey = await crypto.subtle.importKey("raw", new TextEncoder().encode(secretToken), "PBKDF2", false, [ | |
"deriveKey", | |
]); | |
return await crypto.subtle.deriveKey( | |
{ | |
name: "PBKDF2", | |
salt, | |
iterations: 250000, | |
hash: "SHA-256", | |
}, | |
baseKey, | |
{ name: encryptionAlgorithm, length: 256 }, | |
false, | |
keyUsages, | |
); | |
} | |
export async function encrypt( | |
plainText: string, | |
secretToken: string, | |
encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM, | |
) { | |
try { | |
const salt = crypto.getRandomValues(new Uint8Array(16)); | |
const iv = crypto.getRandomValues(new Uint8Array(12)); | |
const aesKey = await buildEncryptionKey(secretToken, salt, ["encrypt"], encryptionAlgorithm); | |
const encryptedContent = await crypto.subtle.encrypt( | |
{ | |
name: encryptionAlgorithm, | |
iv: iv, | |
}, | |
aesKey, | |
new TextEncoder().encode(plainText), | |
); | |
const encryptedContentArr = new Uint8Array(encryptedContent); | |
const buff = new Uint8Array(salt.byteLength + iv.byteLength + encryptedContentArr.byteLength); | |
buff.set(salt, 0); | |
buff.set(iv, salt.byteLength); | |
buff.set(encryptedContentArr, salt.byteLength + iv.byteLength); | |
const base64Buff = btoa(new Uint8Array(buff).reduce((d, byte) => d + String.fromCharCode(byte), "")); | |
return base64Buff; | |
} catch (err) { | |
console.error(`Error in encryptData: ${err}`); | |
return null; | |
} | |
} | |
export async function decrypt( | |
encryptedText: string, | |
secretToken: string, | |
encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM, | |
): Promise<string | null> { | |
try { | |
const encryptedDataBuff = Uint8Array.from(atob(encryptedText), (c) => c.charCodeAt(null)); | |
const salt = encryptedDataBuff.slice(0, 16); | |
const iv = encryptedDataBuff.slice(16, 16 + 12); | |
const bufferedData = encryptedDataBuff.slice(16 + 12); | |
const aesKey = await buildEncryptionKey(secretToken, salt, ["decrypt"], encryptionAlgorithm); | |
const decryptedContent = await crypto.subtle.decrypt( | |
{ | |
name: encryptionAlgorithm, | |
iv: iv, | |
}, | |
aesKey, | |
bufferedData, | |
); | |
return new TextDecoder().decode(decryptedContent); | |
} catch (err) { | |
console.error(`Error in decryptData: ${err}`); | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment