NXP 為了修正 CRYPTO1 的漏洞而引進了更安全的 AES-128(進階加密標準),接下來我們就用 Python + tkinter 來實作一個 AES-128 的加密解密展示工具。
AES-128 是目前全球政府與金融機構(包含新一代的智慧卡 MIFARE DESFire)廣泛使用的對稱式加密標準,安全性極高。
實作前的準備
Python 內建庫沒有完整的 AES 加密,我們需要使用目前最主流、安全的密碼學庫 pycryptodome。請先在終端機執行以下指令安裝:
pip install pycryptodome
AES-128 與 CRYPTO1 的關鍵技術對比
透過這個 GUI 程式,你可以體驗到現代密碼學(AES)與舊型 NXP 密碼(CRYPTO1)本質上的巨大進步:
程式中的核心密碼學概念
方塊填充 (Padding):AES 加密時,不論你的資料多短(即使只輸入一個字),它都必須被填充到 16 位元組的整數倍(如程式中的 pad() 函數),再送入演算法處理。
CBC 模式與 IV (初始向量):在 CBC (Cipher Block Chaining) 模式下,前一個資料塊的密文會與下一個資料塊的明文混合。IV 則是第一個方塊的「引導劑」。這確保了晶片在每次交易時,就算寫入相同的扣款金額,在空氣中被攔截到的無線訊號(HEX 密文)也完全不同,徹底杜絕了防重放攻擊(Replay Attack)。
import tkinter as tk
from tkinter import messagebox, ttk
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
class AES128CryptoApp:
def __init__(self, root):
self.root = root
self.root.title("NXP 升級方案:AES-128 安全加解密系統")
self.root.geometry("620x600")
self.root.resizable(False, False)
# 標題
title_label = tk.Label(root, text="AES-128 (CBC 模式) 密碼學工作台", font=("Arial", 16, "bold"), fg="#1B5E20")
title_label.pack(pady=10)
# 說明
intro_text = "相較於已被破解的 CRYPTO1,AES-128 是目前 NXP 高安全晶片(如 DESFire)的核心演算法。\n本系統展示對稱式區塊加密,包含自動填補(Padding)與初始向量(IV)控制。"
intro_label = tk.Label(root, text=intro_text, font=("Microsoft JhengHei", 9), justify="center", fg="#555")
intro_label.pack(pady=5)
# --- 參數設定區塊 ---
param_frame = tk.LabelFrame(root, text=" 1. AES 密鑰參數設定 (對稱金鑰與 IV) ", font=("Arial", 10, "bold"), padx=10, pady=10)
param_frame.pack(fill="x", padx=20, pady=10)
# 金鑰 (Key) - 16 Bytes
tk.Label(param_frame, text="128-bit 金鑰 (必須剛好 16 個字元/Bytes):").grid(row=0, column=0, sticky="w")
self.key_entry = tk.Entry(param_frame, width=35, font=("Courier", 10))
self.key_entry.insert(0, "NXP_DESFire_Key1")
self.key_entry.grid(row=0, column=1, pady=5, padx=5)
# 初始向量 (IV) - 16 Bytes
tk.Label(param_frame, text="初始向量 IV (必須剛好 16 個字元/Bytes):").grid(row=1, column=0, sticky="w")
self.iv_entry = tk.Entry(param_frame, width=35, font=("Courier", 10))
self.iv_entry.insert(0, "InitVector123456")
self.iv_entry.grid(row=1, column=1, pady=5, padx=5)
# 隨機生成按鈕
rand_btn = tk.Button(param_frame, text="🎲 隨機生成全新 Key & IV", command=self.generate_random_params, bg="#81C784")
rand_btn.grid(row=2, column=0, columnspan=2, pady=5)
# --- 操作區塊 ---
io_frame = tk.LabelFrame(root, text=" 2. 明文輸入與控制 ", font=("Arial", 10, "bold"), padx=10, pady=10)
io_frame.pack(fill="x", padx=20, pady=5)
tk.Label(io_frame, text="請輸入要加密的明文資料 (無長度限制):").pack(anchor="w")
self.plain_entry = tk.Entry(io_frame, width=65, font=("Microsoft JhengHei", 10))
self.plain_entry.insert(0, "NXP 晶片內部機密:帳戶卡片餘額為 $5,200 元")
self.plain_entry.pack(pady=5)
btn_frame = tk.Frame(io_frame)
btn_frame.pack(pady=5)
enc_btn = tk.Button(btn_frame, text="🔒 執行 AES 加密", command=self.aes_encrypt, bg="#E53935", fg="white", font=("Arial", 10, "bold"), width=15)
enc_btn.pack(side="left", padx=10)
dec_btn = tk.Button(btn_frame, text="🔓 執行 AES 解密", command=self.aes_decrypt, bg="#43A047", fg="white", font=("Arial", 10, "bold"), width=15)
dec_btn.pack(side="left", padx=10)
# --- 運算結果區塊 ---
res_frame = tk.LabelFrame(root, text=" 3. 密碼學運算結果顯示區 ", font=("Arial", 10, "bold"), padx=10, pady=10)
res_frame.pack(fill="both", expand=True, padx=20, pady=10)
# 密文顯示
tk.Label(res_frame, text="加密後的密文 (十六進位 HEX 顯示):", fg="#A30000", font=("Arial", 9, "bold")).pack(anchor="w")
self.cipher_output = tk.Text(res_frame, height=3, width=65, bg="#FBE9E7", font=("Courier", 10))
self.cipher_output.pack(pady=5)
# 解密顯示
tk.Label(res_frame, text="解密還原後的明文:", fg="#1B5E20", font=("Arial", 9, "bold")).pack(anchor="w")
self.plain_output = tk.Text(res_frame, height=2, width=65, bg="#E8F5E9", font=("Microsoft JhengHei", 10))
self.plain_output.pack(pady=5)
def generate_random_params(self):
""" 隨機生成強固的 16 位元組密鑰與 IV """
# 使用 os.urandom 生成隨機二進位,並轉為 16 字元的文字
rand_key = os.urandom(16).hex()[:16]
rand_iv = os.urandom(16).hex()[:16]
self.key_entry.delete(0, tk.END)
self.key_entry.insert(0, rand_key)
self.iv_entry.delete(0, tk.END)
self.iv_entry.insert(0, rand_iv)
# 清空下方的結果,避免混淆
self.cipher_output.delete("1.0", tk.END)
self.plain_output.delete("1.0", tk.END)
def get_key_and_iv(self):
""" 讀取並檢查 Key 和 IV 是否符合 AES-128 標準 """
key = self.key_entry.get().encode('utf-8')
iv = self.iv_entry.get().encode('utf-8')
if len(key) != 16 or len(iv) != 16:
messagebox.showerror(
"規格錯誤",
f"AES-128 要求金鑰與 IV 必須剛好為 16 位元組 (16個英文字元)!\n\n"
f"您目前輸入:\n金鑰長度: {len(key)} Bytes\nIV 長度: {len(iv)} Bytes"
)
return None, None
return key, iv
def aes_encrypt(self):
""" AES 加密邏輯 """
key, iv = self.get_key_and_iv()
if not key: return
# 讀取並清空舊結果
plaintext = self.plain_entry.get().encode('utf-8')
self.cipher_output.delete("1.0", tk.END)
self.plain_output.delete("1.0", tk.END) # 加密時順便清空舊的解密結果
if not plaintext:
messagebox.showwarning("提示", "請先輸入明文資料再執行加密!")
return
try:
# 1. 初始化 AES CBC 模式
cipher = AES.new(key, AES.MODE_CBC, iv)
# 2. 自動填充明文至 16 的倍數 (PKCS7 標準)
padded_data = pad(plaintext, AES.block_size)
# 3. 加密
ciphertext = cipher.encrypt(padded_data)
# 將二進位密文轉成 HEX 十六進位字串顯示
hex_cipher = ciphertext.hex().upper()
# 寫入文字框並強制刷新
self.cipher_output.insert("1.0", hex_cipher)
self.root.update_idletasks()
except Exception as e:
messagebox.showerror("加密失敗", f"錯誤原因: {str(e)}")
def aes_decrypt(self):
""" AES 解密邏輯 (修正版:包含強制清空與狀態異常捕獲) """
key, iv = self.get_key_and_iv()
if not key: return
# 1. 獲取密文並去除前後空白與換行
hex_cipher = self.cipher_output.get("1.0", tk.END).strip()
# 2. 【核心修正】每次點擊解密,先強制清空舊的解密文字框
self.plain_output.delete("1.0", tk.END)
if not hex_cipher:
messagebox.showwarning("提示", "密文框內無資料!請先執行加密,或手動填入 HEX 密文。")
return
try:
# 3. 將 HEX 字串還原為二進位
ciphertext = bytes.fromhex(hex_cipher)
# 4. 建立解密器 (對稱加密,Key 與 IV 必須與加密時完全相同)
cipher = AES.new(key, AES.MODE_CBC, iv)
# 5. 解密
decrypted_padded = cipher.decrypt(ciphertext)
# 6. 解除填充 (Unpad) 並用 UTF-8 解碼回人類可讀文字
original_text = unpad(decrypted_padded, AES.block_size).decode('utf-8')
# 7. 寫入解密顯示框,並強制通知 Tkinter GUI 刷新渲染
self.plain_output.insert("1.0", original_text)
self.root.update_idletasks()
except ValueError:
# 密碼學特點:如果 Key/IV 錯了,解密後的區塊尾端不會符合 PKCS7 填充規則,unpad 會直接報 ValueError
messagebox.showerror(
"解密失敗 (密鑰不匹配)",
"無法解密!原因如下:\n"
"1. 您可能點擊了『隨機生成 Key & IV』,導致目前的密鑰與當初加密時的密鑰不一致。\n"
"2. 密文 (HEX) 內容在傳輸或複製時遭到篡改。"
)
except Exception as e:
messagebox.showerror("系統錯誤", f"解密時發生非預期錯誤: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = AES128CryptoApp(root)
root.mainloop()
測試建議步驟(感受密碼學的安全特性)
正常測試:直接點擊
🔒 執行 AES 加密$\to$ 紅色框出現密文 $\to$ 接著點擊🔓 執行 AES 解密$\to$ 綠色框完美還原明文。安全干擾測試(模擬駭客改密鑰):
先點擊
🔒 執行 AES 加密產生密文。此時點擊按鈕
🎲 隨機生成全新 Key & IV(此時金鑰已經變了,但密文還在)。這時候點擊
🔓 執行 AES 解密,程式會立刻捕捉到異常並跳出 「解密失敗 (密鑰不匹配)」 的警告,這證明了 AES 演算法極高的防偽與保密能力!

沒有留言:
張貼留言