Created
July 5, 2024 17:23
-
-
Save ronanyeah/edb1b319aba8e6618cbe584c31257104 to your computer and use it in GitHub Desktop.
Linux OpenSSL + Browser Web Crypto interop
This file contains 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
const opensslPrefix = new TextEncoder().encode("Salted__"); | |
async function encryptBuffer(data: Uint8Array, password: string) { | |
const salt = crypto.getRandomValues(new Uint8Array(8)); | |
const { iv, key } = await passwordToKeyAndIV(password, salt); | |
const encrypted = await crypto.subtle.encrypt( | |
{ name: "AES-CBC", iv: iv }, | |
key, | |
data | |
); | |
const encryptedArray = new Uint8Array(encrypted); | |
const buffer = new Uint8Array( | |
opensslPrefix.length + salt.length + encryptedArray.length | |
); | |
buffer.set(opensslPrefix, 0); | |
buffer.set(salt, opensslPrefix.length); | |
buffer.set(encryptedArray, opensslPrefix.length + salt.length); | |
return encodeBase64(buffer); | |
} | |
async function decryptBuffer(workingBuffer: Uint8Array, password: string) { | |
const encryptedBuffer = workingBuffer.slice(opensslPrefix.length); | |
const salt = encryptedBuffer.slice(0, 8); | |
const encrypted = encryptedBuffer.slice(8); | |
const { key, iv } = await passwordToKeyAndIV(password, salt); | |
const decrypted = await crypto.subtle.decrypt( | |
{ name: "AES-CBC", iv: iv }, | |
key, | |
encrypted | |
); | |
return new TextDecoder().decode(decrypted); | |
} | |
async function passwordToKeyAndIV(password: string, salt: Uint8Array) { | |
const baseKey = await crypto.subtle.importKey( | |
"raw", | |
new TextEncoder().encode(password), | |
{ name: "PBKDF2" }, | |
false, | |
["deriveBits"] | |
); | |
// AES-256-CBC which requires a 256-bit key (32 bytes) + 128-bit IV (16 bytes) | |
const keyLength = 32 * 8; // 32 bytes for AES-256 key | |
const ivLength = 16 * 8; // 16 bytes for AES IV | |
const totalLength = keyLength + ivLength; // Total bits needed | |
const derivedBits = await crypto.subtle.deriveBits( | |
{ | |
name: "PBKDF2", | |
salt: salt, | |
iterations: 100_000, | |
hash: "SHA-256", | |
}, | |
baseKey, | |
totalLength | |
); | |
const keyBuffer = derivedBits.slice(0, 32); // First 32 bytes for the key | |
const ivBuffer = derivedBits.slice(32, 48); // Next 16 bytes for the IV | |
const key = await crypto.subtle.importKey( | |
"raw", | |
keyBuffer, | |
{ name: "AES-CBC", length: 256 }, | |
false, | |
["encrypt", "decrypt"] | |
); | |
return { key, iv: new Uint8Array(ivBuffer) }; | |
} | |
async function encryptText(plaintext: string, password: string) { | |
const encodedText = new TextEncoder().encode(plaintext); | |
return encryptBuffer(encodedText, password); | |
} | |
async function decryptText(encrypted: string, password: string) { | |
return decryptBuffer(decodeBase64(encrypted), password); | |
} | |
function encodeBase64(bytes: Uint8Array): string { | |
return btoa(String.fromCharCode.apply(null, Array.from(bytes))); | |
} | |
function decodeBase64(base64String: string): Uint8Array { | |
return Uint8Array.from(atob(base64String), (c) => c.charCodeAt(0)); | |
} |
This file contains 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
echo "plaintext here" |\ | |
openssl enc -aes-256-cbc -a -iter 100000 -md sha256 -p -salt -pbkdf2 -pass pass:hunter2 | |
echo "U2FsdGVkX19vQGF13Gv2Ce9VsjegORo8guUGKbMmY5A=" |\ | |
openssl enc -aes-256-cbc -iter 100000 -md sha256 -a -d -pbkdf2 -pass pass:hunter2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment