

用 JavaScript 執行 AES 加解密 - 香草口味
source link: https://blog.darkthread.net/blog/vanilla-js-aes-decryption/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用 JavaScript 執行 AES 加解密
最近突發奇想,想將系統查詢結果嵌入網頁匯出成 .html,概念上像 Excel 或 Word 一樣是個文件檔,方便 Email 轉寄、歸檔保存,而採用網頁的好處是免裝軟體,用瀏覽器就能開啟,透過 JavaScript 可實現極佳的互動操作體驗。
但我馬上想到一個問題,針對機敏資料,Excel/Word/PDF 可以加上密碼保護,HTML 檔不行! 我想到的解決方式是比照 Excel/Word 對資料內容加密,檢視網頁時需輸入密碼解密才能讀取內容。這樣的話,JavaScript 端必須有解密能力。
原以為要依賴第三方程式庫,驚喜發現當代瀏覽器很早前就已內建 Web Crypto API,負責低階加解密運算的 subtle API,個人電腦的主要瀏覽器(Chrome/Edge/Firefox/Safari/Opera)都有支援,想在工作環境應用不必擔心瀏覽器不給力。
我的目標很簡單:到用 C# AES 加密的內容,在 JavaScript 能用相同金鑰解密,這樣就能實現與 Word/Excel/PDF 同等級的密碼保護。
借用之前 使用 Bouncy Castle DES/AES 加解密 文章的密碼字串 SHA256 轉 AES Key/IV byte[16] 邏輯,這次的 JavaScript 程式若能用 "#The3ncryp7Key" 將 "QfKvmv2wlAMhqXYM1c5gzLcrf24x+qnMXIwHpNqO4Os="
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AES Encryption/Decryption Demo</title>
<style>
div { padding: 3px; }
input { width: 350px; }
#key { width: 200px; }
#encText,#decText {
color: darkblue;
height: 1em;
}
</style>
</head>
<body>
<div>
Key = <input type="text" id="key" value="#The3ncryp7Key">
</div>
<div>
<input type="text" id="plain" value="Hello World!">
<button onclick="encrypt()">Encrypt</button>
<div id="encText"></div>
</div>
<div>
<input type="text" id="encrypted" value="QfKvmv2wlAMhqXYM1c5gzLcrf24x+qnMXIwHpNqO4Os=">
<button onclick="decrypt()">Decrypt</button>
<div id="decText"></div>
</div>
<script>
if (!window.crypto?.subtle) {
alert("Your browser is unsupported!");
}
async function createCryptoKey(key, keyUsage ) {
const hashBuffer = await window.crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
// split the sha256 hash byte array into key and iv
let keyPart = new Uint8Array(hashBuffer.slice(0, 16));
let ivPart = new Uint8Array(hashBuffer.slice(16));
// create a CryptoKey object from the key byte array
const cryptoKey = await window.crypto.subtle.importKey(
"raw", // format
keyPart, // key data (as a Uint8Array)
{ name: "AES-CBC" }, // algorithm
false, // not extractable
[keyUsage]
);
// return CryptoKey and IV
return { cryptoKey, ivPart };
}
async function encryptData(enc, key) {
const { cryptoKey, ivPart } = await createCryptoKey(key, "encrypt");
const data = new TextEncoder().encode(enc);
const encryptedBytes = await window.crypto.subtle.encrypt(
{ name: "AES-CBC", iv: ivPart },
cryptoKey,
data
);
const encrypted = btoa(String.fromCharCode(...new Uint8Array(encryptedBytes)));
return encrypted;
}
async function decryptData(data, key) {
const { cryptoKey, ivPart } = await createCryptoKey(key, "decrypt");
// Convert the base64-encoded data to a Uint8Array
const dataBytes = new Uint8Array(atob(data).split("").map(c => c.charCodeAt(0)));
// Decrypt the data using the CryptoKey object
const decryptedBytes = await window.crypto.subtle.decrypt(
{ name: "AES-CBC", iv: ivPart },
cryptoKey,
dataBytes
);
return new TextDecoder().decode(decryptedBytes);
}
function encrypt() {
const plain = document.querySelector("#plain").value;
const key = document.querySelector("#key").value;
encryptData(plain, key).then((result) => {
document.querySelector("#encrypted").value = result;
document.querySelector("#encText").textContent = result;
}, (err) => {
console.log(err);
});
}
function decrypt() {
const encrypted = document.querySelector("#encrypted").value;
const key = document.querySelector("#key").value;
decryptData(encrypted, key).then((result) => {
document.querySelector("#decText").textContent = result;
}).catch((err) => {
alert(err.message);
});
}
</script>
</body>
</html>
靠著 Github Copilot 輔助,我先拼湊出半成品,接著依我的需求改成對密碼字串做 SHA256 雜湊產生 Key/IV,修修改改出可執行的版本,接著,爬文搞懂程式碼沒看過的 API 用法,把程式碼改得更簡潔,得到上述的版本。這是我心目中使用 AI 輔助開發的正確姿勢,而非期待描述完需求不動腦就拿到程式,若是如此,代表你的需求是大家早就玩爛的題材。舉凡進階一點的實務需求,都像在探索沒人去過的祕境,你很難期待 AI 直接報路讓你閉著眼睛走到目的地。你必須頭腦清楚,自己辦別方向掌握油門速度,但 Github Copilot 絕對有莫大幫助;AI 吃過的鹽比你吃過的米還多,實戰經驗豐富,隨時依據路況提供實用建議,但接受與否在你,成敗結果自負。優秀的開發者有 AI 協助,能更快完成各種挑戰,但如果什麼都不想學,巴望有了 AI 就能無腦寫完程式,應該會撞牆撞到吐。
總之,Copilot 噴出一堆我沒用過的 API,我花了點時了解:
- SubtleCrypto digest 計算 SHA1(不建議)、SHA-256、SHA-384、SHA-512 雜湊
- SubtleCrypto importKey 建立金鑰,支援 RSA、ECDSA、HYMAC、AES、PBKDF、HKDF 等加密演算法
- Uint8Array 要在 JavaScript 處理 byte[] 必學
- Base64 字串轉 Uint8Array: new Uint8Array(atob(data).split("").map(c => c.charCodeAt(0)))
- TextDecoder.decode()、TextEncoder.encode() 字串與 byte[] 轉換,只支援 UTF-8
實測 "QfKvmv2wlAMhqXYM1c5gzLcrf24x+qnMXIwHpNqO4Os=" 解密成功! (灑花)
附上線上展示。
Recommend
-
71
Häagen·Dazs 哈根达斯 香草味冰淇淋 430g + 赠 G7 杰拉多 提拉米苏冰淇淋 80g 39.9元,来自什么值得买甄选出的中粮我买网优惠产品,汇聚数十万什么值得买网友对该网购产品的点评。
-
49
马迭尔 香草/朗姆/榴莲/芒果/抹茶/香草 冰淇淋 85g *15件 99元包邮(99选15件),来自什么值得买甄选出的京东优惠产品,汇聚数十万什么值得买网友对该网购产品的点评。
-
17
(悬赏100金币)银河香草的交易结构。 一直不理解香草是如何在确保牛市不输于500的情况下,完全保证风险的,既然机构可以这样做(...
-
21
银河香草产品常见问题解答(和银河证券工作人员的沟通结果) 银河香草
-
6
我发现了一个比香草看涨更好的投资机会 2022年,在连续净值上涨3年之后,一方面担忧海外高估崩盘带来危机,又不想错过A股7年没来的牛市,守住...
-
5
买4件29.6伊利 巧乐兹香草巧克力口味脆皮甜筒雪糕冰淇淋冰激凌冷饮 73g*6/盒 买4件29.6
-
9
使用 Bouncy Castle DES/AES 加解密-黑暗執行緒 在 .NET 要執行 DES/AES/RSA 加解密不是難事,遠從 .NET 3.5 時代,System.Security.Cryptography 命名空間都已內建相關 API 提供完整支援。不過這些安全相關 API 高度依賴作業系統的原生程式庫,從 .NET...
-
10
JavaScript前端的AES加密与解密实现 精选 原创 一、CDN插件...
-
10
香草冰激凌吃吗?Android 15内部代号曝光--丁科技网 香草冰激凌吃吗?Android 15内部代号...
-
11
JSON UTC 時間轉 yyyy-MM-mm HH:mm:ss 格式本地時間之香草 JavaScript 極簡解法-黑暗執行緒 生活與工作上遇過多次,從後端接到 2023-07-25T23:30:00Z JSON 格式的 UTC 時間字串,new Date('2023-07-25T23:30:00Z') 轉成 Date 物...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK