어제에 이어서 crypto-js의 AES.encrypt/AES.decrypt 를 구현해봅시다.
import { crypto } from "https://deno.land/std@0.208.0/crypto/mod.ts";
import {
decodeBase64,
encodeBase64,
} from "https://deno.land/std@0.208.0/encoding/base64.ts";
function encodeText(text: string): Uint8Array {
return new TextEncoder().encode(text);
}
async function getSeasoning(
passphrase: string,
salt: ArrayBuffer,
): Promise<{ key: ArrayBuffer; iv: ArrayBuffer }> {
const password = new Uint8Array([
...encodeText(passphrase),
...new Uint8Array(salt),
]);
const md5Hashes: Array<Uint8Array> = [];
let digest = password;
for (let i = 0; i < 3; i++) {
const hash = await crypto.subtle.digest("MD5", digest).then((h) =>
new Uint8Array(h)
);
md5Hashes.push(hash);
digest = new Uint8Array([...hash, ...password]);
}
return {
key: new Uint8Array([...md5Hashes[0], ...md5Hashes[1]]),
iv: md5Hashes[2],
};
}
export async function encrypt(
text: string,
passphrase: string,
): Promise<string> {
const salt = crypto.getRandomValues(new Uint8Array(8));
const { key, iv } = await getSeasoning(passphrase, salt);
const cryptoKey = await crypto.subtle.importKey(
"raw",
key,
{
name: "AES-CBC",
length: 256,
},
true,
["encrypt"],
);
const contents = encodeText(text);
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-CBC",
length: 256,
iv,
},
cryptoKey,
contents,
).then((buffer) => new Uint8Array(buffer));
return encodeBase64(
new Uint8Array([
...encodeText("Salted__"),
...salt,
...encrypted,
]),
);
}
export async function decrypt(
text: string,
passphrase: string,
): Promise<string> {
const decoded = decodeBase64(text);
const salt = decoded.slice(8, 16);
const { key, iv } = await getSeasoning(passphrase, salt);
const cryptoKey = await crypto.subtle.importKey(
"raw",
key,
{
name: "AES-CBC",
length: 256,
},
true,
["decrypt"],
);
const contents = decoded.slice(16);
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-CBC",
length: 256,
iv,
},
cryptoKey,
contents,
).then((buffer) => new Uint8Array(buffer));
return new TextDecoder().decode(decrypted);
}
테스트 코드
import CryptoJS from "npm:crypto-js";
import { decrypt, encrypt } from "./crypto.ts";
import * as assertions from "https://deno.land/std@0.208.0/assert/mod.ts";
const passphrase = "test";
Deno.test("Encryption Test", async () => {
const originalText = "Hello World!";
const cryptoJsEncrypted = CryptoJS.AES.encrypt(originalText, passphrase).toString();
const cryptoEncrypted = await encrypt(originalText, passphrase);
const cryptoJsDecrypted = CryptoJS.AES.decrypt(cryptoEncrypted, passphrase).toString(CryptoJS.enc.Utf8);
const cryptoDecrypted = await decrypt(cryptoJsEncrypted, passphrase);
assertions.assertEquals(cryptoJsDecrypted, originalText);
assertions.assertEquals(cryptoDecrypted, originalText);
})
잘 작동한다.
답글 남기기