語言:繁體中文

tools / aes

aes/

AES 對稱式加密與解密。模式、填補、金鑰與 IV 的格式、字元集都能選,結果和 Java、CryptoJS 一致,金鑰不會離開瀏覽器。

明文

密文

歷史紀錄

每次加密、解密成功後會自動記錄一筆,保留最近 20 筆,只存在這台裝置的瀏覽器裡。不會儲存金鑰,但會儲存 IV 和解密出的明文,在公用電腦上用完請清除紀錄。

AES、DES、RSA 是什麼,有什麼差別

AES 和 DES 是對稱式加密:加密和解密用同一把金鑰,就像同一把鑰匙既能鎖門也能開門。它們速度快,適合加密大量資料,難處在於要怎麼把這把金鑰安全地交給對方。

DES 是 1970 年代的老標準,有效金鑰只有 56 位元,現在用專用硬體或電腦叢集就能暴力破解,只適合用來串接仍在使用它的舊系統。AES 是取代 DES 的新標準,金鑰長度為 128、192 或 256 位元,目前沒有可行的破解方法,是現在加密資料的首選。

RSA 是非對稱式加密:有一對金鑰,公鑰可以公開給任何人,用來加密;私鑰自己保管,用來解密。就像一個誰都能投信、只有你有鑰匙的信箱。好處是不用事先約定金鑰,缺點是很慢,一次只能加密幾百位元組。實際系統常把兩者結合:先用 RSA 加密一把隨機產生的 AES 金鑰傳給對方,再用 AES 加密真正的資料。

加密模式是什麼,該選哪個

AES 每次只能加密 16 位元組,模式決定一長串資料要怎麼切成區塊、區塊與區塊之間怎麼關聯。ECB 每個區塊單獨加密,相同的內容會得到相同的密文,會洩漏資料的規律,不建議使用。CBC 讓每個區塊先和上一個區塊的密文混合再加密,第一個區塊則和 IV 混合,是最常見的選擇。CFB、OFB、CTR 把 AES 變成一串金鑰串流,與資料逐位元組做 XOR,可以不用填補。GCM 在 CTR 的基礎上多算一個驗證標籤(Tag),密文被改動過就會解密失敗,是現在最推薦的模式。

IV 和填補是做什麼用的

IV(初始向量)讓相同的內容每次加密的結果都不一樣。它不需要保密,可以和密文一起傳送,但同一把金鑰下不要重複使用,GCM 尤其如此。除了 ECB,其他模式都需要 IV:GCM 建議 12 位元組,其他模式必須是 16 位元組。

ECB 和 CBC 要求資料長度是 16 位元組的整數倍,不足時依填補方式補齊。PKCS5Padding 和 PKCS7Padding 對 AES 來說完全相同,也是最常用的;ZeroPadding 補 0,解密時會去掉結尾所有的 0 位元組,如果明文本身以 0 位元組結尾,也會一併被去掉;NoPadding 不補,長度要自己確保正確。GCM 不需要填補。

和 Java、CryptoJS 的寫法怎麼對應

Java 的 Cipher.getInstance("AES/CBC/PKCS5Padding") 就是選 CBC 和 PKCS5Padding;只寫 "AES" 時預設是 ECB 和 PKCS5Padding。Java 的 CTR 和 GCM 只能用 NoPadding,GCM 的 Tag 只支援 96 到 128 位元。CryptoJS 的 CryptoJS.pad.Pkcs7 對應 PKCS7Padding,ZeroPadding、AnsiX923、Iso10126、Iso97971 則是同名對應。直接把字串密碼傳給 CryptoJS.AES.encrypt 時,它會自動產生金鑰,並輸出以 U2FsdGVkX1 開頭的結果,這裡不支援這種用法,請改傳入 16、24 或 32 位元組的金鑰和 IV。

GCM 的 Tag 和 AAD 是什麼

Tag 是 GCM 算出來的驗證值,加密結果的最後幾個位元組就是 Tag(128 位元就是 16 位元組)。AAD 是附加驗證資料,例如訊息標頭,它本身不會被加密,但會參與驗證,解密時必須填入相同的內容,否則會提示驗證未通過。

常見問題

出現「金鑰是 N 位元組」或「IV 是 N 位元組」的提示該怎麼辦

先看格式選對了沒:同樣是 32 個字元,選 string 是 32 位元組,選 hex 才是 16 位元組。AES 金鑰必須是 16、24 或 32 位元組,除了 ECB 和 GCM 以外,IV 必須剛好是 16 位元組,這裡不會自動補零或截斷,目前的位元組數顯示在金鑰和 IV 欄位的標題旁。選 string 時位元組數依所選字元集計算,一個中文字在 UTF-8 下佔 3 位元組。如果後端先對密碼做了補零、取雜湊之類的處理,請先算出最終的位元組,再用 hex 或 base64 格式填進來。

解密時提示「填補不正確」該怎麼辦

這代表解出來的最後一個區塊不符合所選的填補規則,通常是金鑰、IV、模式或填補方式有一項和加密時不一致,也可能是密文複製得不完整。請逐項對照加密端的參數,金鑰格式或密文格式(hex 和 base64)選錯也會這樣。ZeroPadding 和 NoPadding 不會檢查填補,選它們不會出現這個錯誤,但參數不對時結果會是亂碼。

解密結果開頭一段是亂碼,或提示「不是有效的 UTF-8 文字」

先檢查 IV。密文超過一個區塊時,CBC 和 CFB 模式下 IV 不對,只會讓第一個 16 位元組的區塊出錯,後面的內容正常,也不會出現填補錯誤,現象就是開頭亂碼、後面是對的。如果提示結果不是有效的文字,也可能是字元集和加密端不一致,例如對方用的是 GBK。把輸出格式改成 hex,可以先確認解出來的位元組本身對不對。

GCM 解密提示「Tag 驗證未通過」該怎麼辦

這代表金鑰、IV、AAD、Tag 長度中有一項和加密時不一致,或是密文被改動過。這裡會把輸入的最後一段當作 Tag,長度依「Tag 長度」選項而定(預設 128 位元,也就是 16 位元組),所以密文和 Tag 要連在一起填入;如果對方把 Tag 單獨給你,請先接在密文後面。AAD 的格式預設是 hex,填入 string 內容時記得改格式;加密時沒填 AAD,解密時也要留空。

加密結果和 Java、Python 等後端對不上,該怎麼找出原因

請依這個順序核對:模式和填補、金鑰和 IV 的格式與位元組數、字元集、輸入輸出格式。string 格式的明文不會去掉頭尾空白,結尾多一個換行就是另一段資料。CTR、CFB、OFB 在這裡也會依所選的填補補齊,選 PKCS5Padding 時密文會比明文長,後端用 NoPadding 的話,這裡也要選 NoPadding。這裡的 CFB 是 128 位元整區塊回饋,對方用 CFB8 的話會對不上。

密文該選 hex 還是 base64,選錯了會怎樣

照對方的輸出來選:全是 0-9、a-f 的是 hex,有大小寫字母、+、/ 或結尾有 = 的是 base64。解密時選錯不一定馬上出錯,因為 hex 用到的字元都是合法的 base64 字元,只是解出來的位元組不對,最後會表現為「不是 16 的整數倍」或「填補不正確」。base64 裡的 - 和 _(URL 安全寫法)以及省略的 = 都能辨識,hex 裡的空格、冒號和 0x 前綴會被忽略。

歷史紀錄裡存了什麼,會上傳嗎

只存在這台裝置瀏覽器的 localStorage 裡,不會上傳,頁面程式碼裡也沒有任何網路請求。每次成功加密或解密後記錄一筆,最多保留 20 筆,輸入或結果超過 2 萬字元的不記錄。紀錄裡有參數、IV、AAD、輸入和結果,以及一個由金鑰算出的 8 個字元的指紋(每筆紀錄中 # 號後面那串字元,用來分辨是不是同一把金鑰),不會儲存金鑰本身,所以點「填入」之後要重新填入金鑰。每筆都可以單獨刪除,在公用電腦上用完請點「清除紀錄」。

可以自動產生隨機金鑰或 IV 嗎

不行。這裡沒有產生按鈕,金鑰和 IV 要自己填。相同的明文、金鑰、IV 和參數,每次加密的結果都一樣,只有 ISO10126 填補會混入隨機位元組而使結果不同。需要隨機值時可以自己產生,例如在命令列執行 openssl rand -hex 16 會輸出 16 位元組,格式選 hex 填進來即可。