系統概述
本系統是一個基于AES-256-CBC加密算法的身份證號碼加解密工具(手搓底層步驟),針對的是上一篇文章對的AES加密原理的講解,雖說是演示,但功能完善,可單獨提供接口給項目調用,采用Python開發,具有圖形化用戶界面。系統實現了完整的AES-256加密算法,支持多密鑰管理,并提供了安全可靠的身份證號碼加密和解密功能。
源碼請查看頂部綁定資源完整源碼文件
或底部源碼自行拷貝
上一篇文章鏈接:
AES加密算法原理講解--基于AES-256加密算法實現身份證號碼的加解密程序項目講解_aes256 64位key反算密碼如何實現-CSDN博客
主要特性
- 高強度加密:采用AES-256-CBC加密模式,提供高級別的數據安全保護
- 完整實現:從底層實現AES算法的所有組件,包括S盒、輪常量、密鑰擴展等
- 多密鑰支持:支持生成、導入和管理多個加密密鑰
- 密鑰指紋識別:通過SHA256哈希生成密鑰指紋,確保密鑰匹配
- 歷史記錄管理:自動保存加解密歷史,避免重復操作
- 現代化界面:基于CustomTkinter的美觀用戶界面,支持明暗主題切換
技術架構
核心算法組件
1. AES-256核心實現
AES256類是系統的核心加密引擎,實現了完整的AES-256算法:
- 字節替換(SubBytes):使用標準S盒進行非線性字節替換
- 行移位(ShiftRows):對狀態矩陣的行進行循環移位
- 列混淆(MixColumns):通過矩陣乘法實現列間數據擴散
- 輪密鑰加(AddRoundKey):將輪密鑰與狀態矩陣異或
- 密鑰擴展:將256位主密鑰擴展為15個128位輪密鑰
2. CBC模式封裝
AES256_CBC類實現了密碼塊鏈接模式:
- PKCS7填充:自動處理數據塊對齊
- 初始向量(IV):每次加密生成隨機16字節IV
- 鏈式加密:前一個密文塊參與下一個明文塊的加密
3. 密鑰管理系統
- JSON格式存儲:密鑰以JSON格式持久化存儲
- 時間戳記錄:記錄每個密鑰的生成時間
- 指紋驗證:使用SHA256前4字節作為密鑰指紋
- 自動匹配:解密時自動匹配正確的密鑰
系統安裝與運行
環境要求
# 必需的Python包,但是只有最后一個需要安裝,其余的不需要(內置)環境為python3.7以上
import datetime
import json
import os
import base64
import hashlib
import tkinter as tk
from tkinter import messagebox, simpledialog, ttk
import customtkinter as ctk
安裝步驟
- 安裝CustomTkinter:
- pip install customtkinter
- 運行程序:
- python AES-256-CBC.py(代碼命令)
詳細操作指南
主界面功能
啟動程序后,您將看到簡潔的主界面,包含兩個主要功能按鈕:
- 加密身份證:進入身份證號碼加密功能
- 解密身份證:進入密文解密功能
加密操作流程
1. 進入加密界面
點擊"加密身份證"按鈕,進入加密功能界面。界面包含以下區域:
- 密鑰管理區域:顯示當前密鑰指紋,提供密鑰生成和導入功能
- 輸入區域:身份證號碼輸入框
- 輸出區域:顯示加密結果
- 密鑰歷史區域:顯示所有歷史密鑰,支持雙擊切換
2. 密鑰管理
生成新密鑰:
- 點擊"生成新密鑰"按鈕
- 系統自動生成256位隨機密鑰
- 密鑰自動保存到aes_keys.json文件
- 界面顯示新密鑰的指紋信息
導入自定義密鑰:
- 點擊"導入密鑰"按鈕
- 在彈出對話框中輸入64位十六進制密鑰(32字節)
- 系統驗證密鑰格式并檢查是否重復
- 成功導入后更新當前使用的密鑰
切換歷史密鑰:
- 在密鑰歷史表格中雙擊任意密鑰記錄
- 系統自動切換到選中的密鑰
- 當前密鑰指紋顯示更新
3. 執行加密
- 輸入身份證號碼:
- 在輸入框中輸入18位身份證號碼
- 系統自動驗證格式(前17位數字,第18位數字或X)
- 點擊加密按鈕:
- 系統生成隨機16字節初始向量(IV)
- 使用當前密鑰執行AES-256-CBC加密
- 組合IV、密鑰指紋和密文
- 進行Base64編碼便于存儲和傳輸
- 查看結果:
- 加密成功后,密文顯示在輸出區域
- 密文自動保存到ciphertext1.txt文件
- 每個密文占用一行,便于批量管理
4. 加密數據格式
生成的密文采用以下格式:
[16字節IV] + [4字節密鑰指紋] + [實際密文數據]
經過Base64編碼后的最終格式便于存儲和傳輸。
解密操作流程
1. 進入解密界面
點擊"解密身份證"按鈕,進入解密功能界面。界面包含:
- 密鑰文件路徑顯示:顯示密鑰存儲文件的絕對路徑
- 當前密鑰指紋顯示:顯示正在使用的密鑰信息
- 文件操作區域:密文文件加載和輸入清空功能
- 密文列表:顯示已保存的所有密文
- 密文輸入區域:手動輸入或顯示選中的密文
- 解密按鈕區域:默認密鑰和自定義密鑰解密選項
- 結果輸出區域:顯示解密結果
2. 加載密文數據
從文件加載:
- 點擊"選擇密文文件"按鈕
- 系統自動讀取ciphertext1.txt文件
- 所有密文顯示在列表中(顯示前30個字符)
- 雙擊列表項可將完整密文加載到輸入區域
手動輸入:
- 直接在密文輸入區域粘貼或輸入Base64編碼的密文
- 支持多行密文輸入
3. 執行解密
使用默認密鑰解密:
- 確保密文已輸入到輸入區域
- 點擊"使用默認密鑰解密"按鈕
- 系統自動:
- 解析密文中的密鑰指紋
- 在密鑰庫中查找匹配的密鑰
- 提取IV和實際密文數據
- 執行AES-256-CBC解密
- 顯示解密后的身份證號碼
使用自定義密鑰解密:
- 點擊"使用自定義密鑰解密"按鈕
- 在彈出對話框中輸入64位十六進制密鑰
- 系統驗證密鑰與密文指紋是否匹配
- 匹配成功則執行解密并顯示結果
4. 解密結果處理
- 成功解密:在結果區域顯示"解密成功!身份證號碼: [號碼]"
- 自動記錄:解密記錄保存到plaintext_history.json文件
- 重復檢測:系統檢測是否已解密過相同密文,避免重復記錄
- 錯誤處理:密鑰不匹配或數據損壞時顯示具體錯誤信息
文件系統說明
核心文件
- AES-256-CBC.py:主程序文件,包含所有功能實現
- aes_keys.json:密鑰存儲文件,JSON格式
- ciphertext1.txt:加密后的密文存儲文件
- plaintext_history.json:解密歷史記錄文件
密鑰文件格式
[
??{
????"timestamp": "2024-01-15 10:30:45",
????"key": "64位十六進制密鑰字符串",
????"fingerprint": "8位十六進制指紋"
??}
]
解密歷史格式
[
??{
????"timestamp": "2024-01-15 10:35:20",
????"plaintext": "解密后的身份證號碼",
????"ciphertext": "原始密文",
????"key_fingerprint": "使用的密鑰指紋",
????"key_type": "default或custom"
??}
]
安全特性
加密安全性
- AES-256標準:采用美國國家標準技術研究所(NIST)認證的AES-256算法
- CBC模式:密碼塊鏈接模式提供更強的安全性
- 隨機IV:每次加密使用不同的初始向量,防止相同明文產生相同密文
- 密鑰指紋:SHA256哈希確保密鑰完整性驗證
數據保護
- 本地存儲:所有數據存儲在本地,不涉及網絡傳輸
- 密鑰分離:密鑰與密文分別存儲,提高安全性
- 格式驗證:嚴格的輸入格式驗證防止無效數據
- 錯誤處理:完善的異常處理機制保護系統穩定性
常見問題解答
Q1: 忘記密鑰怎么辦?
答:如果忘記了自定義密鑰,可以通過以下方式找回:
- 查看aes_keys.json文件中的歷史密鑰
- 根據密鑰指紋匹配正確的密鑰
- 如果密鑰文件丟失,則無法恢復加密數據
Q2: 密文文件損壞如何處理?
答:
- 檢查密文是否為完整的Base64編碼
- 確認密文長度是否正確(至少20字節的Base64編碼)
- 驗證使用的密鑰是否與加密時一致
- 如果數據確實損壞,則無法恢復
Q3: 如何備份重要數據?
答:建議定期備份以下文件:
- aes_keys.json?- 密鑰文件(最重要)
- ciphertext1.txt?- 密文文件
- plaintext_history.json?- 解密歷史
Q4: 程序運行出錯怎么辦?
答:
- 檢查Python環境和依賴包是否正確安裝
- 確認CustomTkinter版本兼容性
- 查看錯誤提示信息,通常會指出具體問題
- 刪除損壞的配置文件,程序會自動重新創建
使用建議
最佳實踐
- 定期備份密鑰:將aes_keys.json文件備份到安全位置
- 使用強密鑰:導入自定義密鑰時確保隨機性和復雜度
- 驗證結果:加密后立即測試解密確保數據完整性
- 安全存儲:將密鑰文件存儲在加密的存儲設備中
性能優化
- 批量操作:對于大量數據,建議分批處理
- 內存管理:處理大文件時注意內存使用情況
- 文件清理:定期清理不需要的歷史記錄文件
注意:本系統僅用于合法的數據保護目的,請遵守相關法律法規,不得用于非法用途。加密的身份證信息應當妥善保管,防止泄露。
源碼:
import datetime
import json
import os
import base64
import hashlib
import tkinter as tk
from tkinter import messagebox, simpledialog, ttk
import customtkinter as ctk# 明文保存文件路徑 - 用于存儲解密后的明文記錄
PLAINTEXT_FILE = "plaintext_history.json"# 設置CustomTkinter的外觀模式和默認顏色主題
ctk.set_appearance_mode("System") # 系統模式,自動適應明暗主題
ctk.set_default_color_theme("blue") # 使用藍色主題# ====================== AES-256 完整實現 ======================
# ----------------------- 常量定義 -----------------------
# 加密字節代換表(S盒),AES標準定義的非線性替換表,用于加密時的字節替換
# 每個字節(0x00-0xFF)對應一個替換值,提供非線性變換特性
S_BOX = (0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
)# 解密字節代換表(逆S盒),用于解密時的字節逆替換
# 與S_BOX互為逆操作,確保sbox[inv_sbox[x]] = x
INV_S_BOX = (0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
)# 輪常量表(Round Constants),用于密鑰擴展算法中生成輪密鑰
# 每個元素對應GF(2?)上的x^(i-1)值,用于消除密鑰的對稱性
RCON = (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36)# 加密列混淆左乘矩陣(標準AES定義)
# 用于加密時對每列進行矩陣乘法,實現擴散效果
MIX_MATRIX = [[0x02, 0x03, 0x01, 0x01],[0x01, 0x02, 0x03, 0x01],[0x01, 0x01, 0x02, 0x03],[0x03, 0x01, 0x01, 0x02]
]
# 解密列混淆左乘矩陣(標準AES逆矩陣)
# 用于解密時恢復原始列數據
INV_MIX_MATRIX = [[0x0E, 0x0B, 0x0D, 0x09],[0x09, 0x0E, 0x0B, 0x0D],[0x0D, 0x09, 0x0E, 0x0B],[0x0B, 0x0D, 0x09, 0x0E]
]# ----------------------- 核心算法類 -----------------------
class AES256:def __init__(self, key):# 初始化時生成輪密鑰,key為32字節(256位)原始密鑰self.round_keys = self.key_expansion(key)@staticmethoddef sub_bytes(s):"""加密字節替換:使用S盒替換狀態矩陣中的每個字節"""for i in range(4): # 遍歷4行for j in range(4): # 遍歷4列s[i][j] = S_BOX[s[i][j]] # 查表替換@staticmethoddef inv_sub_bytes(s):"""解密字節逆替換:使用逆S盒恢復原始字節"""for i in range(4):for j in range(4):s[i][j] = INV_S_BOX[s[i][j]]@staticmethoddef shift_rows(s):"""加密行移位:對每行進行循環左移操作"""# 第0行不移位s[1] = s[1][1:] + s[1][:1] # 第1行左移1字節s[2] = s[2][2:] + s[2][:2] # 第2行左移2字節s[3] = s[3][3:] + s[3][:3] # 第3行左移3字節@staticmethoddef inv_shift_rows(s):"""解密行逆移位:恢復原始行位置"""# 第0行不變s[1] = s[1][3:] + s[1][:3] # 第1行右移1字節(等同左移3字節)s[2] = s[2][2:] + s[2][:2] # 第2行右移2字節(等同左移2字節)s[3] = s[3][1:] + s[3][:1] # 第3行右移3字節(等同左移1字節)def mix_columns(self, s):"""加密列混淆:對每列進行矩陣乘法"""for i in range(4): # 處理每一列col = [s[0][i], s[1][i], s[2][i], s[3][i]] # 提取列數據new_col = [0] * 4 # 初始化新列# 矩陣乘法(4x4矩陣 * 4x1列向量)for row in range(4):for elem in range(4):# 累加伽羅瓦域乘法結果new_col[row] ^= self.ffmul(MIX_MATRIX[row][elem], col[elem])# 更新狀態矩陣s[0][i] = new_col[0]s[1][i] = new_col[1]s[2][i] = new_col[2]s[3][i] = new_col[3]# 下面為手動展開計算可以消除循環判斷的開銷。# for i in range(4):# s0 = s[0][i]# s1 = s[1][i]# s2 = s[2][i]# s3 = s[3][i]# s[0][i] = self.ffmul(0x02, s0) ^ self.ffmul(0x03, s1) ^ s2 ^ s3# s[1][i] = s0 ^ self.ffmul(0x02, s1) ^ self.ffmul(0x03, s2) ^ s3# s[2][i] = s0 ^ s1 ^ self.ffmul(0x02, s2) ^ self.ffmul(0x03, s3)# s[3][i] = self.ffmul(0x03, s0) ^ s1 ^ s2 ^ self.ffmul(0x02, s3)def inv_mix_columns(self, s):"""解密列混淆:使用逆矩陣恢復原始列數據"""for i in range(4): # 處理每一列col = [s[0][i], s[1][i], s[2][i], s[3][i]]new_col = [0] * 4for row in range(4):for elem in range(4):new_col[row] ^= self.ffmul(INV_MIX_MATRIX[row][elem], col[elem])# 更新狀態矩陣s[0][i] = new_col[0]s[1][i] = new_col[1]s[2][i] = new_col[2]s[3][i] = new_col[3]# for i in range(4):# s0 = s[0][i]# s1 = s[1][i]# s2 = s[2][i]# s3 = s[3][i]# s[0][i] = self.ffmul(0x0e, s0) ^ self.ffmul(0x0b, s1) ^ self.ffmul(0x0d, s2) ^ self.ffmul(0x09, s3)# s[1][i] = self.ffmul(0x09, s0) ^ self.ffmul(0x0e, s1) ^ self.ffmul(0x0b, s2) ^ self.ffmul(0x0d, s3)# s[2][i] = self.ffmul(0x0d, s0) ^ self.ffmul(0x09, s1) ^ self.ffmul(0x0e, s2) ^ self.ffmul(0x0b, s3)# s[3][i] = self.ffmul(0x0b, s0) ^ self.ffmul(0x0d, s1) ^ self.ffmul(0x09, s2) ^ self.ffmul(0x0e, s3)@staticmethoddef ffmul(a, b):"""伽羅瓦域乘法(GF(2?)):計算a和b在有限域上的乘積"""p = 0for _ in range(8): # 遍歷b的每一位if b & 1: # 如果最低位為1p ^= a # 異或累加a <<= 1 # 左移相當于乘以xif a & 0x100: # 處理模溢出a ^= 0x11b # 異或不可約多項式x? + x? + x3 + x + 1(0x11b)b >>= 1 # 處理下一位return pdef key_expansion(self, key):"""密鑰擴展:將32字節密鑰擴展為15個輪密鑰(每個16字節)"""key_bytes = list(key) # 將密鑰轉換為字節列表nk = 8 # AES-256的初始密鑰字數(256位=8字)nr = 14 # AES-256的總輪數# 初始密鑰復制expanded_key = key_bytes.copy()# 擴展循環(生成后續輪密鑰)for i in range(nk, 4 * (nr + 1)):temp = expanded_key[4 * i - 4: 4 * i] # 獲取前4字節# 每8個字的特殊處理(AES-256特有)if i % nk == 0:# RotWord:循環左移1字節temp = temp[1:] + temp[:1]# SubWord:S盒替換temp = [S_BOX[b] for b in temp]# 異或輪常數temp[0] ^= RCON[i // nk - 1]elif nk == 8 and i % nk == 4:# 中間輪的特殊處理(每8個字進行一次S盒替換)temp = [S_BOX[b] for b in temp]# 生成新字:與前nk個字異或new_word = [expanded_key[4 * (i - nk)] ^ temp[0],expanded_key[4 * (i - nk) + 1] ^ temp[1],expanded_key[4 * (i - nk) + 2] ^ temp[2],expanded_key[4 * (i - nk) + 3] ^ temp[3]]expanded_key.extend(new_word) # 添加到擴展密鑰# 分割為15個輪密鑰(每個16字節)return [expanded_key[i * 16: (i + 1) * 16] for i in range(nr + 1)]def encrypt_block(self, plaintext):"""加密單個數據塊(16字節)"""# 將輸入字節轉換為4x4狀態矩陣state = [list(plaintext[i:i + 4]) for i in range(0, 16, 4)]# 初始輪密鑰加self.add_round_key(state, self.round_keys[0])# 主輪次(共14輪)for i in range(1, 14):self.sub_bytes(state) # 字節替換self.shift_rows(state) # 行移位self.mix_columns(state) # 列混淆self.add_round_key(state, self.round_keys[i]) # 輪密鑰加# 最終輪(無列混淆)self.sub_bytes(state)self.shift_rows(state)self.add_round_key(state, self.round_keys[14])# 將狀態矩陣轉換回字節序列return bytes(sum(state, []))def decrypt_block(self, ciphertext):"""解密單個數據塊(16字節)"""state = [list(ciphertext[i:i + 4]) for i in range(0, 16, 4)]# 初始輪密鑰加(使用最后一個輪密鑰)self.add_round_key(state, self.round_keys[14]) # 輪密鑰加self.inv_shift_rows(state) # 逆行移位self.inv_sub_bytes(state) # 逆字節替換# 主輪次(逆序處理)for i in range(13, 0, -1):self.add_round_key(state, self.round_keys[i]) # 輪密鑰加self.inv_mix_columns(state) # 逆列混淆self.inv_shift_rows(state) # 逆行移位self.inv_sub_bytes(state) # 逆字節替換# 最終輪密鑰加self.add_round_key(state, self.round_keys[0])return bytes(sum(state, []))@staticmethoddef add_round_key(s, k):"""輪密鑰加:將輪密鑰與狀態矩陣異或"""for i in range(4):for j in range(4):# 按列順序訪問輪密鑰s[i][j] ^= k[i + j * 4]# ----------------------- CBC 模式封裝 -----------------------
class AES256_CBC:def __init__(self, key):# 初始化AES-256核心算法實例self.aes = AES256(key) # 傳入密鑰初始化加密器@staticmethoddef pkcs7_pad(data):"""PKCS7填充:將數據填充至16字節的倍數"""pad_len = 16 - (len(data) % 16) # 計算需要填充的字節數return data + bytes([pad_len] * pad_len) # 填充重復的字節值為pad_len@staticmethoddef pkcs7_unpad(data):"""PKCS7去除填充:驗證并去除填充數據"""pad_len = data[-1] # 最后一個字節為填充長度return data[:-pad_len] # 去除填充部分def encrypt(self, plaintext, iv):"""CBC模式加密:處理整個明文并生成密文"""padded = self.pkcs7_pad(plaintext) # 填充數據# 將數據分割為16字節的塊blocks = [padded[i:i + 16] for i in range(0, len(padded), 16)]cipher_blocks = [] # 存儲加密后的塊prev = iv # 初始向量作為首個塊的異或值for block in blocks:# CBC模式核心:與前一個密文塊(或IV)異或后再加密xored = bytes([b1 ^ b2 for b1, b2 in zip(prev, block)])encrypted = self.aes.encrypt_block(xored)cipher_blocks.append(encrypted)prev = encrypted # 更新前一個塊為當前密文return b''.join(cipher_blocks) # 拼接所有密文塊def decrypt(self, ciphertext, iv):"""CBC模式解密:處理密文并恢復原始明文"""# 分割密文為16字節塊blocks = [ciphertext[i:i + 16] for i in range(0, len(ciphertext), 16)]plain_blocks = []prev = iv # 初始向量用于首個塊for block in blocks:# 解密當前塊decrypted = self.aes.decrypt_block(block)# 與前一個密文塊異或得到原始明文塊plain = bytes([b1 ^ b2 for b1, b2 in zip(prev, decrypted)])plain_blocks.append(plain)prev = block # 更新前一個塊為當前密文(注意是原始密文,非解密結果)# 拼接并去除填充return self.pkcs7_unpad(b''.join(plain_blocks))# ====================== 應用程序接口 ======================
KEY_FILE = "aes_keys.json" # JSON格式存儲多密鑰
CIPHER_FILE = "ciphertext1.txt" # 密文存儲文件名def generate_aes_key():"""生成并保存隨機AES-256密鑰到JSON文件"""import datetimekey = os.urandom(32)print(f'當前使用的初始密鑰為:{key.hex()}')timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")key_hex = key.hex()fingerprint = hashlib.sha256(key).digest()[:4].hex()# 讀取現有密鑰或初始化空列表try:with open(KEY_FILE, "r") as f:keys = json.load(f)except (FileNotFoundError, json.JSONDecodeError):keys = []# 添加新密鑰條目keys.append({"timestamp": timestamp,"key": key_hex,"fingerprint": fingerprint})# 保存到文件with open(KEY_FILE, "w") as f:json.dump(keys, f, indent=2)return keydef import_custom_key(key_str):"""導入自定義密鑰到JSON文件"""# 驗證輸入格式:必須是64位十六進制字符(32字節)if len(key_str) != 64 or not all(c in "0123456789abcdef" for c in key_str):raise ValueError("密鑰必須是64位十六進制字符") # 64位十六進制也就是32字節# 將十六進制字符串轉換為字節對象(32字節)key = bytes.fromhex(key_str)# 生成時間戳(用于記錄密鑰創建時間)timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")# 生成密鑰指紋(SHA256哈希前4字節的十六進制)fingerprint = hashlib.sha256(key).digest()[:4].hex()# 讀取現有密鑰文件try:with open(KEY_FILE, "r") as f:keys = json.load(f) # 嘗試加載JSON格式的密鑰列表except (FileNotFoundError, json.JSONDecodeError):keys = [] # 文件不存在或格式錯誤時初始化空列表# 檢查密鑰是否已存在(通過十六進制字符串比對)if any(entry["key"] == key_str for entry in keys):raise ValueError("該密鑰已存在")# 添加新密鑰條目到列表keys.append({"timestamp": timestamp, # 記錄創建時間"key": key_str, # 原始十六進制密鑰字符串"fingerprint": fingerprint # 指紋用于快速識別})print(f'當前使用的初始密鑰為:{key_str}')# 寫回更新后的密鑰列表到文件with open(KEY_FILE, "w") as f:json.dump(keys, f, indent=2) # 帶縮進的JSON格式便于閱讀return key # 返回字節類型的密鑰對象供后續使用def load_aes_key():"""加載最新密鑰,沒有則自動生成返回:bytes: 最新密鑰的字節形式(32字節)處理邏輯:1. 檢查密鑰文件是否存在,不存在則生成新密鑰2. 讀取并解析JSON格式的密鑰文件3. 異常處理:文件損壞時刪除并生成新密鑰4. 按時間戳降序排列,返回最新密鑰"""if not os.path.exists(KEY_FILE):return generate_aes_key() # 無密鑰文件時生成新密鑰try:with open(KEY_FILE, "r") as f:keys = json.load(f) # 加載JSON格式的密鑰列表if not keys: # 空文件檢查return generate_aes_key()# 按時間戳降序排序(最新密鑰在前)keys_sorted = sorted(keys, key=lambda x: x["timestamp"], reverse=True)return bytes.fromhex(keys_sorted[0]["key"]) # 轉換最新密鑰為字節except Exception as e: # 捕獲JSON解析錯誤等異常messagebox.showwarning("錯誤", f"密鑰文件損壞: {str(e)}")os.remove(KEY_FILE) # 刪除損壞文件return generate_aes_key() # 生成替代密鑰def get_all_keys():"""獲取所有密鑰信息返回:list: 包含所有密鑰字典的列表,每個字典包含:- timestamp: 生成時間字符串- key: 64位十六進制密鑰字符串- fingerprint: 4字節指紋的十六進制異常處理:- 文件不存在返回空列表- JSON解析失敗視為空列表"""try:with open(KEY_FILE, "r") as f:return json.load(f) # 直接返回解析后的密鑰列表except (FileNotFoundError, json.JSONDecodeError): # 處理文件異常return [] # 返回空列表保持程序健壯性def get_key_fingerprint(key):"""生成密鑰指紋:SHA256哈希的前4字節參數:key (bytes): 原始密鑰字節返回:bytes: 4字節指紋(用于密文組合)說明:- print用于調試顯示當前指紋- 前4字節在保證唯一性的前提下縮短數據長度"""print(f'當前指紋為:{hashlib.sha256(key).digest()[:4].hex()}') # 調試輸出return hashlib.sha256(key).digest()[:4] # 取哈希值前4字節作為指紋def encrypt_id_number(plaintext, key):"""加密身份證號:生成IV并返回Base64編碼的完整密文"""iv = os.urandom(16) # 生成16字節隨機初始向量,每塊為16字節,所以生成16字節的iv,用于與第一塊異或print(f'本次加密的初始化向量為:{iv.hex()}')cipher = AES256_CBC(key)ciphertext = cipher.encrypt(plaintext.encode(), iv) # 執行加密# 組合IV、密鑰指紋和密文combined = iv + get_key_fingerprint(key) + ciphertextprint(f'未進行base64編碼前的組合:{combined.hex()}')return base64.b64encode(combined).decode() # Base64編碼便于存儲def decrypt_id_number(ciphertext_b64):"""解密Base64編碼的密文:驗證指紋并返回明文"""combined = base64.b64decode(ciphertext_b64) # 解碼# 拆分各部分:前16字節IV,接下來4字節指紋,剩余為密文iv, fingerprint, ciphertext = combined[:16], combined[16:20], combined[20:]keys = get_all_keys()for entry in keys:if bytes.fromhex(entry["fingerprint"]) == fingerprint: # 匹配指紋cipher = AES256_CBC(bytes.fromhex(entry["key"]))key = bytes.fromhex(entry["key"]) # 所用密鑰return cipher.decrypt(ciphertext, iv).decode(), key # 執行解密轉換為字符串并返回結果,同時返回密鑰raise ValueError("未找到匹配的密鑰")def decrypt_with_key(ciphertext_b64, key):"""使用指定密鑰解密Base64編碼的密文:驗證指紋并返回明文"""combined = base64.b64decode(ciphertext_b64) # 解碼# 拆分各部分:前16字節IV,接下來4字節指紋,剩余為密文iv, fingerprint, ciphertext = combined[:16], combined[16:20], combined[20:]# 驗證密鑰指紋匹配if fingerprint != get_key_fingerprint(key):raise ValueError("密鑰與密文不匹配")cipher = AES256_CBC(key)plaintext = cipher.decrypt(ciphertext, iv) # 執行解密return plaintext.decode() # 轉換為字符串# ====================== GUI 界面部分 ======================
class MainPage(ctk.CTkFrame):'''主界面'''def __init__(self, master, show_encrypt, show_decrypt):super().__init__(master)# 創建標題標簽title_label = ctk.CTkLabel(self,text="身份證號加解密系統",font=ctk.CTkFont(family="微軟雅黑", size=24, weight="bold"),text_color=("gray10", "gray90"))title_label.pack(pady=30)# 創建按鈕容器button_frame = ctk.CTkFrame(self, fg_color="transparent")button_frame.pack(pady=20)# 加密按鈕encrypt_btn = ctk.CTkButton(button_frame,text="🔒 加密身份證",command=show_encrypt,width=200,height=40,font=ctk.CTkFont(size=14),fg_color=("blue", "blue"),hover_color=("darkblue", "darkblue"),corner_radius=10)encrypt_btn.pack(pady=10)# 解密按鈕decrypt_btn = ctk.CTkButton(button_frame,text="🔓 解密身份證",command=show_decrypt,width=200,height=40,font=ctk.CTkFont(size=14),fg_color=("green", "green"),hover_color=("darkgreen", "darkgreen"),corner_radius=10)decrypt_btn.pack(pady=10)class KeyManagementMixin:"""密鑰管理功能混入類功能:- 提供密鑰生成、導入的GUI控件- 管理密鑰顯示和狀態更新- 集成到需要密鑰管理功能的頁面中特性:- 可復用組件,通過繼承方式為其他界面添加密鑰管理能力- 自動同步密鑰狀態到主應用實例"""def create_key_controls(self, parent):"""創建密鑰管理界面組件參數:parent: 父容器控件構建:1. 創建包含兩個操作按鈕的水平工具欄(生成/導入)2. 創建密鑰指紋顯示標簽3. 布局到父容器中"""key_frame = ctk.CTkFrame(parent, fg_color=("gray90", "gray15"), corner_radius=10)# 密鑰操作按鈕容器(水平排列)btn_frame = ctk.CTkFrame(key_frame, fg_color="transparent")# 生成新密鑰按鈕ctk.CTkButton(btn_frame,text="生成新密鑰",command=self.generate_new_key,width=120,height=35,font=ctk.CTkFont(size=14),fg_color=("blue", "blue"),hover_color=("darkblue", "darkblue"),corner_radius=8).pack(side="left", padx=5)# 導入密鑰按鈕ctk.CTkButton(btn_frame,text="導入密鑰",command=self.import_key_dialog,width=120,height=35,font=ctk.CTkFont(size=14),fg_color=("green", "green"),hover_color=("darkgreen", "darkgreen"),corner_radius=8).pack(side="left", padx=5)btn_frame.pack(pady=10)# 密鑰指紋顯示標簽self.fingerprint_label = ctk.CTkLabel(key_frame,text="",font=ctk.CTkFont(size=14),anchor="w")self.fingerprint_label.pack(anchor="w", padx=10, pady=5)# 將密鑰管理面板打包到父容器key_frame.pack(fill="x", padx=10, pady=10)def generate_new_key(self):"""執行密鑰生成流程流程:1. 調用generate_aes_key生成新密鑰(同時寫入文件)2. 更新主應用實例的當前密鑰3. 刷新界面顯示4. 彈出操作成功提示"""new_key = generate_aes_key() # 生成并持久化新密鑰self.master.key = new_key # 更新主應用的密鑰引用self.update_key_display(new_key) # 刷新指紋顯示messagebox.showinfo("成功", "新密鑰已生成并保存!") # 用戶反饋def import_key_dialog(self):"""處理密鑰導入操作流程:1. 彈出輸入對話框獲取密鑰字符串2. 輸入為空時直接返回3. 調用import_custom_key驗證并存儲密鑰4. 成功更新主應用密鑰,失敗顯示錯誤詳情"""key_str = simpledialog.askstring("導入密鑰", "請輸入64位十六進制密鑰:") # 64位十六進制也就是32字節if not key_str: return # 用戶取消輸入try:# 轉換輸入為小寫并驗證格式key = import_custom_key(key_str.lower()) # 可能拋出ValueErrorself.master.key = key # 更新主應用密鑰self.update_key_display(key) # 刷新顯示messagebox.showinfo("成功", "密鑰導入成功!")except Exception as e:# 顯示具體錯誤信息(如格式錯誤、已存在等)messagebox.showerror("錯誤", f"密鑰導入失敗:{str(e)}")def update_key_display(self, key):"""更新密鑰指紋顯示參數:key: bytes類型密鑰(32字節)操作:1. 計算密鑰的SHA256指紋(取前4字節)2. 轉換為十六進制字符串3. 更新標簽文本"""fingerprint = get_key_fingerprint(key).hex() # 獲取4字節指紋的hex字符串self.fingerprint_label.config(text=f"當前密鑰指紋:{fingerprint}") # 更新顯示class EncryptPage(KeyManagementMixin, ctk.CTkFrame):"""加密功能主界面,繼承密鑰管理功能和Frame容器特性"""def __init__(self, master, key, go_back):# 初始化父類框架和混入類super().__init__(master, fg_color="transparent")# 保持對主應用窗口的引用self.master = master# 存儲當前使用的密鑰(bytes類型)self.key = key# 構建界面元素self.build_ui(go_back)# 初始化顯示當前密鑰信息self.update_key_display(key)def build_ui(self, go_back):"""構建界面布局"""# 返回按鈕(右上角定位)ctk.CTkButton(self,text="← 返回",command=go_back,width=100,height=30,corner_radius=8,fg_color=("gray60", "gray30"),hover_color=("gray50", "gray40")).pack(anchor="ne", padx=10)# 創建密鑰管理控件(來自KeyManagementMixin)self.create_key_controls(self)# 輸入區域容器input_frame = ctk.CTkFrame(self, fg_color="transparent")# 身份證號標簽ctk.CTkLabel(input_frame,text="身份證號碼(18位):",font=ctk.CTkFont(size=14)).pack(anchor="w")# 帶實時驗證的輸入框self.entry = ctk.CTkEntry(input_frame,width=300,height=35,placeholder_text="請輸入18位身份證號",font=ctk.CTkFont(size=14),corner_radius=8)self.entry.pack(pady=5)# 加密按鈕ctk.CTkButton(input_frame,text="加密",command=self.encrypt,width=120,height=35,font=ctk.CTkFont(size=14),fg_color=("blue", "blue"),hover_color=("darkblue", "darkblue"),corner_radius=8).pack(pady=10)input_frame.pack(pady=10)# 輸出區域self.output = ctk.CTkTextbox(self,height=100,width=600,font=ctk.CTkFont(size=13),corner_radius=8,border_width=1)self.output.pack(pady=10)self.output.configure(state="disabled")# 創建密鑰歷史面板self.create_key_history()def create_key_history(self):"""初始化密鑰歷史記錄面板"""# 創建帶標題的容器框架self.history_frame = ctk.CTkFrame(self,fg_color=("gray90", "gray15"),corner_radius=10)# 添加標題標簽ctk.CTkLabel(self.history_frame,text="密鑰歷史(雙擊選擇)",font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 5), padx=10, anchor="w")# 由于CustomTkinter沒有直接對應的Treeview組件,我們使用原生tkinter的Treeview# 但是調整其樣式以匹配CustomTkinter的外觀self.tree = tk.ttk.Treeview(self.history_frame,columns=("timestamp", "fingerprint", "key_preview"),show="headings",selectmode="browse",height=6)# 列配置self.tree.column("timestamp", width=180, anchor="w")self.tree.column("fingerprint", width=100, anchor="w")self.tree.column("key_preview", width=120, anchor="w")# 表頭文本設置self.tree.heading("timestamp", text="生成時間")self.tree.heading("fingerprint", text="指紋")self.tree.heading("key_preview", text="密鑰預覽")# 創建垂直滾動條scrollbar = tk.ttk.Scrollbar(self.history_frame,orient="vertical",command=self.tree.yview)self.tree.configure(yscrollcommand=scrollbar.set)# 布局表格和滾動條self.tree.pack(side="left", fill="both", expand=True, padx=10, pady=10)scrollbar.pack(side="right", fill="y", pady=10)self.history_frame.pack(fill="both", expand=True, padx=10, pady=10)# 綁定鼠標雙擊事件self.tree.bind("<Double-1>", self.on_key_selected)# 初始加載歷史數據self.update_key_history()def on_key_selected(self, event):"""處理密鑰選擇事件"""# 獲取選中的第一行(唯一選擇)item = self.tree.selection()[0]# 從條目tag屬性獲取完整HEX密鑰key_hex = self.tree.item(item, "tags")[0]try:# 轉換HEX字符串為字節對象self.master.key = bytes.fromhex(key_hex)# 更新當前頁面顯示self.update_key_display(self.master.key)# 設置選中狀態(藍色高亮)self.tree.selection_set(item)# 聚焦到選中行(灰色背景)self.tree.focus(item)# 操作成功提示messagebox.showinfo("成功", "已切換加密密鑰!")except Exception as e:# 捕獲轉換失敗等異常messagebox.showerror("錯誤", f"密鑰加載失敗: {str(e)}")def update_key_display(self, key):"""更新密鑰信息顯示"""# 生成4字節指紋(HEX字符串)fingerprint = hashlib.sha256(key).digest()[:4].hex()# 截取前8位字符+省略號key_preview = key.hex()[:8] + "..."# 更新標簽顯示self.fingerprint_label.configure(text=f"當前加密密鑰:\n指紋: {fingerprint}\n密鑰預覽: {key_preview}")def update_key_history(self):"""刷新密鑰歷史記錄"""# 清空現有條目for item in self.tree.get_children():self.tree.delete(item)# 獲取所有密鑰并按時間倒序排列keys = sorted(get_all_keys(), key=lambda x: x["timestamp"], reverse=True)# 遍歷插入新數據for entry in keys:# 生成密鑰預覽(前8位+省略號)key_preview = f"{entry['key'][:8]}..."# 插入新行,tag存儲完整HEX密鑰self.tree.insert("", # 根節點"end", # 插入位置values=( # 三列數據entry["timestamp"],entry["fingerprint"],key_preview),tags=(entry["key"],) # 隱藏數據存儲)def generate_new_key(self):"""生成新密鑰(覆蓋父類方法)"""super().generate_new_key() # 調用父類生成邏輯app = self.winfo_toplevel() # 獲取Application實例app.load_key() # 重新加載最新密鑰self.key = app.key # 同步當前頁面引用self.update_key_history() # 刷新歷史記錄self.update_key_display(app.key) # 更新顯示def import_key_dialog(self):"""導入密鑰(覆蓋父類方法)"""super().import_key_dialog() # 調用父類導入邏輯app = self.winfo_toplevel() # 獲取Application實例app.load_key() # 重新加載密鑰self.update_key_history() # 刷新歷史面板self.update_key_display(app.key) # 更新顯示def validate_input(self, text):"""實時驗證身份證輸入參數:text: 當前輸入框內容返回:bool: 是否有效"""# 長度超過18無效# if len(text) > 18:# return False# 空輸入允許(中間輸入狀態)# if len(text) == 0:# return True# 前17位必須為數字# if not text[:17].isdigit():# return False# 第18位校驗(數字或X)# if len(text) == 18 and not (text[17].isdigit() or text[17].lower() == 'x'):# return Falsereturn Truedef encrypt(self):"""執行加密操作"""# 獲取輸入并轉為大寫id_num = self.entry.get().upper()# 最終長度驗證if len(id_num) != 18:messagebox.showerror("錯誤", "請輸入有效的18位身份證號碼")returnif not id_num[:17].isdigit():messagebox.showerror("錯誤", "前17位必須為數字")returnif not (id_num[17].isdigit() or id_num[17] in ['X', 'x']):messagebox.showerror("錯誤", "第18位必須為數字或X")returntry:# 獲取Application實例(替代直接使用self.master,解決CTkFrame沒有key屬性的問題)app = self.winfo_toplevel()# 使用Application實例中的密鑰進行加密ciphertext = encrypt_id_number(id_num, app.key)# 追加寫入密文文件,每個密文占一行with open(CIPHER_FILE, "a") as f:f.write(ciphertext + "\n")# 顯示操作結果,包括生成的密文self.show_output(f"加密成功!密文已保存\n{ciphertext}")except Exception as e:# 捕獲并顯示加密過程中的任何錯誤messagebox.showerror("錯誤", str(e))def show_output(self, text):"""更新輸出區域內容"""self.output.configure(state="normal")self.output.delete("1.0", "end")self.output.insert("end", text)self.output.configure(state="disabled")class DecryptPage(ctk.CTkFrame):"""解密功能主界面功能:- 顯示密鑰存儲路徑- 加載并選擇加密文件- 支持默認密鑰和自定義密鑰解密- 顯示解密結果- 保存解密記錄到JSON文件"""PLAINTEXT_FILE = "plaintext_history.json" # 明文保存文件路徑def check_ciphertext_exists(self, ciphertext):"""檢查密文是否已存在于歷史記錄中參數:ciphertext: 要檢查的密文返回:bool: 如果存在返回True,否則返回False"""try:# 檢查明文歷史記錄文件是否存在if not os.path.exists(self.PLAINTEXT_FILE):return False# 讀取歷史記錄文件內容with open(self.PLAINTEXT_FILE, "r") as f:history = json.load(f)# 遍歷歷史記錄,檢查密文是否存在for record in history:if record.get("ciphertext") == ciphertext:return True# 未找到匹配的密文return Falseexcept Exception:# 發生任何錯誤時返回False,確保程序繼續運行return Falsedef save_plaintext_record(self, plaintext, key_fingerprint, key_type, ciphertext):"""保存明文記錄到JSON文件參數:plaintext: 解密后的明文key_fingerprint: 使用的密鑰指紋key_type: 密鑰類型("default"或"custom")ciphertext: 原始密文"""try:# 獲取當前時間并格式化為指定格式current_time = datetime.datetime.now()# 將時間格式化為 "YYYY-MM-DD HH:MM:SS" 格式formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")# 創建包含所有必要信息的記錄字典record = {"timestamp": formatted_time, # 格式化的時間戳"plaintext": plaintext, # 解密后的明文"ciphertext": ciphertext, # 原始密文"key_fingerprint": key_fingerprint, # 使用的密鑰指紋"key_type": key_type # 密鑰類型(默認或自定義)}# 讀取現有歷史記錄或創建新的記錄列表if os.path.exists(self.PLAINTEXT_FILE):# 如果文件存在,讀取現有記錄with open(self.PLAINTEXT_FILE, "r") as f:history = json.load(f)else:# 如果文件不存在,創建空列表history = []# 將新記錄添加到歷史記錄列表中history.append(record)# 將更新后的歷史記錄寫回文件,使用縮進格式化JSONwith open(self.PLAINTEXT_FILE, "w") as f:json.dump(history, f, indent=2)except Exception as e:# 如果保存過程中出現錯誤,顯示錯誤消息messagebox.showerror("錯誤", f"保存明文記錄失敗: {str(e)}")def __init__(self, master, key, go_back):"""初始化解密頁面參數:master: 父容器(應用主窗口)key: 初始密鑰(bytes)go_back: 返回主界面的回調函數"""super().__init__(master, fg_color="transparent")self.last_custom_key = None # 緩存最后一次成功使用的自定義密鑰# 顯示密鑰文件絕對路徑ctk.CTkLabel(self,text=f"密鑰文件路徑: {os.path.abspath(KEY_FILE)}",font=ctk.CTkFont(size=12),anchor="w").pack(anchor="nw", padx=10, pady=5)self.cipher_list = [] # 存儲從文件加載的原始密文數據self.key = key # 當前使用的默認密鑰self.fingerprint_label = None # 指紋顯示標簽的引用占位self.build_ui(go_back) # 構建界面元素self.update_fingerprint(self.key) # 初始化顯示默認密鑰指紋def build_ui(self, go_back):"""構建界面布局"""# 返回按鈕(右上角定位)ctk.CTkButton(self,text="← 返回",command=go_back,width=100,height=30,corner_radius=8,fg_color=("gray60", "gray30"),hover_color=("gray50", "gray40")).pack(anchor="ne", padx=10)# 密鑰指紋顯示標簽self.fingerprint_label = ctk.CTkLabel(self,text="",font=ctk.CTkFont(size=14),anchor="w")self.fingerprint_label.pack(anchor="nw", padx=10, pady=5)# 操作按鈕容器input_frame = ctk.CTkFrame(self, fg_color="transparent")# 文件選擇按鈕ctk.CTkButton(input_frame,text="選擇密文文件",command=self.load_file,width=120,height=35,font=ctk.CTkFont(size=14),fg_color=("blue", "blue"),hover_color=("darkblue", "darkblue"),corner_radius=8).pack(side="left", padx=5)# 清空輸入按鈕ctk.CTkButton(input_frame,text="清空輸入",command=self.clear_input,width=120,height=35,font=ctk.CTkFont(size=14),fg_color=("gray60", "gray30"),hover_color=("gray50", "gray40"),corner_radius=8).pack(side="left", padx=5)input_frame.pack(pady=10)# 密文列表框list_frame = ctk.CTkFrame(self, fg_color=("gray90", "gray15"), corner_radius=10)list_frame.pack(pady=10, padx=20, fill="x")ctk.CTkLabel(list_frame,text="已保存的密文:",font=ctk.CTkFont(size=14)).pack(anchor="w", padx=10, pady=5)# 使用傳統Listbox,因為CustomTkinter沒有直接對應的組件self.listbox = tk.Listbox(list_frame,height=6,width=70,bg=("gray95" if ctk.get_appearance_mode() == "light" else "gray20"),fg=("black" if ctk.get_appearance_mode() == "light" else "white"),font=("Segoe UI", 10),borderwidth=0,highlightthickness=1,highlightcolor=("gray70" if ctk.get_appearance_mode() == "light" else "gray40"))self.listbox.bind("<<ListboxSelect>>", self.select_cipher)self.listbox.pack(pady=5, padx=10, fill="x")# 密文輸入/展示區域input_area_frame = ctk.CTkFrame(self, fg_color="transparent")input_area_frame.pack(pady=10, fill="x", padx=20)ctk.CTkLabel(input_area_frame,text="密文輸入:",font=ctk.CTkFont(size=14)).pack(anchor="w")self.input = ctk.CTkTextbox(input_area_frame,height=60, # 進一步減小高度width=600,font=ctk.CTkFont(size=13),corner_radius=8,border_width=1)self.input.pack(pady=5, fill="x")# 操作按鈕容器button_frame = ctk.CTkFrame(self, fg_color="transparent")button_frame.pack(pady=10)# 默認密鑰解密按鈕ctk.CTkButton(button_frame,text="使用默認密鑰解密",command=self.decrypt,width=180,height=35,font=ctk.CTkFont(size=14),fg_color=("blue", "blue"),hover_color=("darkblue", "darkblue"),corner_radius=8).pack(side="left", padx=10)# 自定義密鑰解密按鈕ctk.CTkButton(button_frame,text="使用自定義密鑰解密",command=self.decrypt_with_custom_key,width=180,height=35,font=ctk.CTkFont(size=14),fg_color=("green", "green"),hover_color=("darkgreen", "darkgreen"),corner_radius=8).pack(side="left", padx=10)# 結果輸出區域output_frame = ctk.CTkFrame(self, fg_color="transparent")output_frame.pack(pady=10, fill="x", padx=20)ctk.CTkLabel(output_frame,text="解密結果:",font=ctk.CTkFont(size=14)).pack(anchor="w")self.output = ctk.CTkTextbox(output_frame,height=170,width=600,font=ctk.CTkFont(size=13),corner_radius=8,border_width=1)self.output.pack(pady=5, fill="x")self.output.configure(state="disabled")def update_fingerprint(self, key):"""更新當前密鑰指紋顯示"""fingerprint = get_key_fingerprint(key).hex()self.fingerprint_label.configure(text=f"當前密鑰指紋: {fingerprint}")def decrypt_with_custom_key(self):"""使用自定義密鑰解密流程"""ciphertext = self.input.get("1.0", "end").strip()if not ciphertext:messagebox.showerror("錯誤", "請輸入或選擇要解密的密文")returnif not self.last_custom_key:self.last_custom_key = self._get_valid_custom_key()if not self.last_custom_key:return# 檢查密文是否已在歷史記錄中存在if self.check_ciphertext_exists(ciphertext):# 顯示提示信息,告知用戶密文已解密過messagebox.showinfo("提示", "當前密文已解密過,請查看歷史記錄")try:# 仍然解密以顯示結果,但不保存記錄plaintext = decrypt_with_key(ciphertext, self.last_custom_key)# 更新密鑰指紋顯示self.update_fingerprint(self.last_custom_key)# 顯示解密結果self.show_output(f"解密成功!\n身份證號碼: {plaintext}")except Exception as e:# 解密失敗時清除無效的自定義密鑰緩存self.last_custom_key = None# 恢復默認密鑰顯示self.update_fingerprint(self.key)# 顯示錯誤信息self.show_output(f"解密失敗: {str(e)}")# 提前返回,不保存記錄(避免重復記錄)returntry:# 使用自定義密鑰解密plaintext = decrypt_with_key(ciphertext, self.last_custom_key)# 獲取密鑰指紋的十六進制表示fingerprint = get_key_fingerprint(self.last_custom_key).hex()# 更新密鑰指紋顯示self.update_fingerprint(self.last_custom_key)# 顯示解密結果self.show_output(f"解密成功!\n身份證號碼: {plaintext}")# 保存解密記錄到JSON文件self.save_plaintext_record(plaintext, fingerprint, "custom", ciphertext)except Exception as e:# 解密失敗時清除無效的自定義密鑰緩存self.last_custom_key = None# 恢復默認密鑰顯示self.update_fingerprint(self.key)# 顯示錯誤信息self.show_output(f"解密失敗: {str(e)}")def _get_valid_custom_key(self):"""獲取有效自定義密鑰"""while True:key_str = simpledialog.askstring("自定義密鑰", "請輸入64位十六進制密鑰:")if not key_str:return Nonetry:if len(key_str) != 64 or not all(c in "0123456789abcdef" for c in key_str.lower()):raise ValueError("密鑰必須是64位十六進制字符")return bytes.fromhex(key_str.lower())except Exception as e:messagebox.showerror("輸入錯誤", f"無效密鑰: {str(e)}")continuedef load_file(self):"""加載密文文件到列表控件"""try:with open(CIPHER_FILE, "r") as f:self.cipher_list = [line.strip() for line in f if line.strip()]self.listbox.delete(0, "end")for c in self.cipher_list:self.listbox.insert("end", f"{c[:30]}...")except Exception as e:messagebox.showerror("錯誤", f"文件讀取失敗: {str(e)}")def select_cipher(self, event):"""處理列表項選擇事件"""idx = self.listbox.curselection()if not idx:returnfull_text = self.cipher_list[idx[0]]self.input.delete("1.0", "end")self.input.insert("1.0", full_text)def clear_input(self):"""清空輸入框內容"""self.input.delete("1.0", "end")def decrypt(self):"""使用默認密鑰的解密流程"""# 獲取輸入框中的密文并去除首尾空白ciphertext = self.input.get("1.0", "end").strip()# 檢查密文是否為空if not ciphertext:messagebox.showerror("錯誤", "請輸入或選擇要解密的密文")return# 檢查密文是否已在歷史記錄中存在if self.check_ciphertext_exists(ciphertext):# 顯示提示信息,告知用戶密文已解密過messagebox.showinfo("提示", "當前密文已解密過,請查看歷史記錄")try:# 仍然解密以顯示結果,但不保存記錄plaintext, used_key = decrypt_id_number(ciphertext)# 更新當前使用的密鑰self.key = used_key# 更新密鑰指紋顯示self.update_fingerprint(self.key)# 顯示解密結果self.show_output(f"解密成功!\n身份證號碼: {plaintext}")except Exception as e:# 解密失敗時更新顯示并顯示錯誤信息self.update_fingerprint(self.key)self.show_output(f"解密失敗: {str(e)}")# 提前返回,不保存記錄(避免重復記錄)returntry:# 使用默認密鑰解密plaintext, used_key = decrypt_id_number(ciphertext)# 更新當前使用的密鑰self.key = used_key# 獲取密鑰指紋的十六進制表示fingerprint = get_key_fingerprint(used_key).hex()# 更新密鑰指紋顯示self.update_fingerprint(self.key)# 顯示解密結果self.show_output(f"解密成功!\n身份證號碼: {plaintext}")# 保存解密記錄到JSON文件self.save_plaintext_record(plaintext, fingerprint, "default", ciphertext)except Exception as e:# 解密失敗時更新顯示并顯示錯誤信息self.update_fingerprint(self.key)self.show_output(f"解密失敗: {str(e)}")def show_output(self, text):"""更新結果輸出區域"""self.output.configure(state="normal")self.output.delete("1.0", "end")self.output.insert("1.0", text)self.output.configure(state="disabled")class Application(ctk.CTk):"""主應用程序類,繼承自CustomTkinter的CTk窗口"""def __init__(self):"""初始化應用程序實例"""super().__init__() # 調用父類CTk的構造函數# 配置窗口self.title("身份證號加解密系統 v3.0")self.geometry("800x650")# 設置主題ctk.set_appearance_mode("system") # 系統主題(自動適應明暗)ctk.set_default_color_theme("blue") # 使用藍色主題# 配置窗口樣式self.configure(fg_color=("gray95", "gray10")) # 背景顏色(亮/暗模式)# 加載密鑰self.key = load_aes_key()# 初始化界面self.init_ui()def init_ui(self):"""初始化用戶界面組件"""# 創建主容器self.container = ctk.CTkFrame(self, fg_color="transparent")self.container.pack(fill="both", expand=True, padx=20, pady=20)# 創建主頁面實例self.main_page = MainPage(self.container, self.show_encrypt, self.show_decrypt)self.encrypt_page = Noneself.decrypt_page = None# 顯示主頁面self.show_main()def show_main(self):"""顯示主頁面"""self._hide_pages()self.main_page.pack(fill="both", expand=True)def show_encrypt(self):"""顯示加密頁面"""self._hide_pages()self.encrypt_page = EncryptPage(self.container, self.key, self.show_main)self.encrypt_page.pack(fill="both", expand=True)def show_decrypt(self):"""顯示解密頁面"""self._hide_pages()self.decrypt_page = DecryptPage(self.container, self.key, self.show_main)self.decrypt_page.pack(fill="both", expand=True)def _hide_pages(self):"""隱藏所有Frame類型的子組件"""for widget in self.container.winfo_children():widget.pack_forget()def load_key(self):"""重新加載當前密鑰"""self.key = load_aes_key()def generate_new_key(self):"""生成全新密鑰并更新狀態"""generate_aes_key()self.load_key()if __name__ == "__main__":"""程序入口點"""app = Application() # 創建應用程序實例app.mainloop() # 啟動Tkinter事件循環