系统概述

本系统是一个基于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

安装步骤

  1. 安装CustomTkinter
  • pip install customtkinter
  1. 运行程序
  • python AES-256-CBC.py(代码命令)

详细操作指南

主界面功能

启动程序后,您将看到简洁的主界面,包含两个主要功能按钮:

  • 加密身份证:进入身份证号码加密功能
  • 解密身份证:进入密文解密功能

加密操作流程

1. 进入加密界面

点击"加密身份证"按钮,进入加密功能界面。界面包含以下区域:

  • 密钥管理区域:显示当前密钥指纹,提供密钥生成和导入功能
  • 输入区域:身份证号码输入框
  • 输出区域:显示加密结果
  • 密钥历史区域:显示所有历史密钥,支持双击切换

2. 密钥管理

生成新密钥

  • 点击"生成新密钥"按钮
  • 系统自动生成256位随机密钥
  • 密钥自动保存到aes_keys.json文件
  • 界面显示新密钥的指纹信息

导入自定义密钥

  • 点击"导入密钥"按钮
  • 在弹出对话框中输入64位十六进制密钥(32字节)
  • 系统验证密钥格式并检查是否重复
  • 成功导入后更新当前使用的密钥

切换历史密钥

  • 在密钥历史表格中双击任意密钥记录
  • 系统自动切换到选中的密钥
  • 当前密钥指纹显示更新
3. 执行加密
  1. 输入身份证号码
    • 在输入框中输入18位身份证号码
    • 系统自动验证格式(前17位数字,第18位数字或X)
  2. 点击加密按钮
    • 系统生成随机16字节初始向量(IV)
    • 使用当前密钥执行AES-256-CBC加密
    • 组合IV、密钥指纹和密文
    • 进行Base64编码便于存储和传输
  3. 查看结果
    • 加密成功后,密文显示在输出区域
    • 密文自动保存到ciphertext1.txt文件
    • 每个密文占用一行,便于批量管理
4. 加密数据格式

生成的密文采用以下格式:

[16字节IV] + [4字节密钥指纹] + [实际密文数据]

经过Base64编码后的最终格式便于存储和传输。

解密操作流程

1. 进入解密界面

点击"解密身份证"按钮,进入解密功能界面。界面包含:

  • 密钥文件路径显示:显示密钥存储文件的绝对路径
  • 当前密钥指纹显示:显示正在使用的密钥信息
  • 文件操作区域:密文文件加载和输入清空功能
  • 密文列表:显示已保存的所有密文
  • 密文输入区域:手动输入或显示选中的密文
  • 解密按钮区域:默认密钥和自定义密钥解密选项
  • 结果输出区域:显示解密结果

2. 加载密文数据

从文件加载

  • 点击"选择密文文件"按钮
  • 系统自动读取ciphertext1.txt文件
  • 所有密文显示在列表中(显示前30个字符)
  • 双击列表项可将完整密文加载到输入区域

手动输入

  • 直接在密文输入区域粘贴或输入Base64编码的密文
  • 支持多行密文输入
3. 执行解密

使用默认密钥解密

  1. 确保密文已输入到输入区域
  2. 点击"使用默认密钥解密"按钮
  3. 系统自动:
    • 解析密文中的密钥指纹
    • 在密钥库中查找匹配的密钥
    • 提取IV和实际密文数据
    • 执行AES-256-CBC解密
    • 显示解密后的身份证号码

使用自定义密钥解密

  1. 点击"使用自定义密钥解密"按钮
  2. 在弹出对话框中输入64位十六进制密钥
  3. 系统验证密钥与密文指纹是否匹配
  4. 匹配成功则执行解密并显示结果
4. 解密结果处理
  • 成功解密:在结果区域显示"解密成功!身份证号码: [号码]"
  • 自动记录:解密记录保存到plaintext_history.json文件
  • 重复检测:系统检测是否已解密过相同密文,避免重复记录
  • 错误处理:密钥不匹配或数据损坏时显示具体错误信息

文件系统说明

核心文件

  1. AES-256-CBC.py:主程序文件,包含所有功能实现
  2. aes_keys.json:密钥存储文件,JSON格式
  3. ciphertext1.txt:加密后的密文存储文件
  4. 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"
  }
]

安全特性

加密安全性

  1. AES-256标准:采用美国国家标准技术研究所(NIST)认证的AES-256算法
  2. CBC模式:密码块链接模式提供更强的安全性
  3. 随机IV:每次加密使用不同的初始向量,防止相同明文产生相同密文
  4. 密钥指纹:SHA256哈希确保密钥完整性验证

数据保护

  1. 本地存储:所有数据存储在本地,不涉及网络传输
  2. 密钥分离:密钥与密文分别存储,提高安全性
  3. 格式验证:严格的输入格式验证防止无效数据
  4. 错误处理:完善的异常处理机制保护系统稳定性

常见问题解答

Q1: 忘记密钥怎么办?

:如果忘记了自定义密钥,可以通过以下方式找回:

  1. 查看aes_keys.json文件中的历史密钥
  2. 根据密钥指纹匹配正确的密钥
  3. 如果密钥文件丢失,则无法恢复加密数据

Q2: 密文文件损坏如何处理?

  1. 检查密文是否为完整的Base64编码
  2. 确认密文长度是否正确(至少20字节的Base64编码)
  3. 验证使用的密钥是否与加密时一致
  4. 如果数据确实损坏,则无法恢复

Q3: 如何备份重要数据?

:建议定期备份以下文件:

  1. aes_keys.json - 密钥文件(最重要)
  2. ciphertext1.txt - 密文文件
  3. plaintext_history.json - 解密历史

Q4: 程序运行出错怎么办?

  1. 检查Python环境和依赖包是否正确安装
  2. 确认CustomTkinter版本兼容性
  3. 查看错误提示信息,通常会指出具体问题
  4. 删除损坏的配置文件,程序会自动重新创建

使用建议

最佳实践

  1. 定期备份密钥:将aes_keys.json文件备份到安全位置
  2. 使用强密钥:导入自定义密钥时确保随机性和复杂度
  3. 验证结果:加密后立即测试解密确保数据完整性
  4. 安全存储:将密钥文件存储在加密的存储设备中

性能优化

  1. 批量操作:对于大量数据,建议分批处理
  2. 内存管理:处理大文件时注意内存使用情况
  3. 文件清理:定期清理不需要的历史记录文件


注意:本系统仅用于合法的数据保护目的,请遵守相关法律法规,不得用于非法用途。加密的身份证信息应当妥善保管,防止泄露。

源码:

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⁴ + x³ + 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事件循环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98043.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98043.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98043.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LangChain: Models, Prompts 模型和提示词

获取openapikey #!pip install python-dotenv #!pip install openai import osimport openai ​ from dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv()) # read local .env file openai.api_key os.environ[OPENAI_API_KEY] # account for deprecat…

ACMESSL自动续签教程

目录 1、选择申请证书 ​编辑2、选择CA机构 ​编辑3、选择自动验签 ​编辑4、证书续签设置 5、自动发布设置 本教程实现ACMESSL自动续签&#xff0c;请按照此教程实现。 1、选择申请证书 点击快捷入口或者订单或证书列表中的【创建证书】按钮&#xff1a; 2、选择CA机构 …

基于飞算JavaAI的在线图书借阅平台设计实现

项目概述与需求分析 1.1 项目背景与意义 随着数字化时代的快速发展&#xff0c;传统图书馆管理模式已无法满足现代读者的需求。在线图书借阅平台通过互联网技术将图书资源数字化&#xff0c;为读者提供便捷的检索、借阅和管理服务&#xff0c;有效解决了传统图书馆开放时间有…

通过API接口管理企业微信通讯录案例

1.开始前需要登录企业微信管理员后台&#xff0c;开启通讯录同步&#xff0c;同时添加企业可信IP地址&#xff0c;记录下Secret信息和企业ID&#xff0c;后面的程序会用到这两个参数。2.下面是用python写的创建企业微信账号的具体案例。#!/usr/bin/env python3 # -*- coding: u…

硬件开发_基于物联网的自动售卖机系统

一.系统概述 物联网自动售卖机系统的主要功能如下&#xff1a; 核心控制器&#xff1a;采用STM32单片机作为系统核心&#xff0c;负责整体数据处理和各设备的统一控制。商品选择&#xff1a;支持语音识别及按键方式&#xff0c;方便用户在售卖机内选择商品。语音播报&#xff1…

AGENTS.md: AI编码代理的开放标准

每个项目都有一个 README.md 文件供人类阅读。但随着 AI 编码代理和 AI 辅助开发的兴起,我们需要一个新标准:AGENTS.md。这个 Markdown 文件定义了代理如何构建、测试和协作。 这就是 AGENTS.md 的作用。 它是一个简单的 Markdown 文件,告诉 AI 助手如何在你的项目中操作:…

如何解决 OutOfMemoryError 内存溢出 —— 原因、定位与解决方案

网罗开发&#xff08;小红书、快手、视频号同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

阿里云服务器配置ssl-docker nginx

# 切换到您当前的目录 cd /AAAAAAAAAAAA# 创建存放nginx配置、证书和日志的目录结构 mkdir -p nginx-config/conf.d nginx-ssl nginx-logs# 为挂载做准备&#xff0c;您可能需要将当前dist目录内容移动到新的html目录 # 首先查看当前dist目录的内容 ls -la dist/# 如果html目录…

2025全球生成式引擎优化(GEO)服务商发展趋势与企业赋能白皮书

引言&#xff1a;人工智能技术的迅猛发展&#xff0c;特别是在生成式AI领域的突破&#xff0c;正以前所未有的力量重塑商业世界的竞争格局。对于寻求提升在线可见性、优化品牌互动及实现可持续增长的企业而言&#xff0c;生成式引擎优化&#xff08;GEO&#xff09;已然成为数字…

海康威视工业相机SDK开发实战:使用C/C++实现软件触发图像采集(含详细中文注释代码)

一、前言 在机器视觉、自动化检测、智能制造等领域&#xff0c;工业相机是获取图像数据的核心设备。海康威视作为国内领先的机器视觉厂商&#xff0c;其工业相机产品线丰富&#xff0c;广泛应用于各类工业场景。 本文将带你从零开始&#xff0c;使用 海康MVS SDK&#xff08;Ma…

Modbus RTU 协议介绍

Modbus RTU 协议介绍 异步串行传输方式&#xff0c;采用二进制格式&#xff0c;适用于串行通讯&#xff08;如RS-485&#xff09;&#xff0c;效率高&#xff0c;是工业现场的主流选择。 主站是Master&#xff0c;从站是Slave。 Modbus RTU 协议格式 帧结构 地址码&#xf…

TCP/IP函数——sendmsg

sendmsg() 是 POSIX 标准中一个高级套接字发送函数,属于系统调用(由操作系统内核实现),定义在 <sys/socket.h> 头文件中。它的核心特点是支持复杂消息结构,不仅能发送常规数据,还能附加控制信息(如辅助数据、IP 选项等),适用于 TCP、UDP 等多种协议,功能比 sen…

运动控制中的插值运动(插补运动):原理、实现与应用

在自动化设备中,从起点到终点的精准轨迹控制是核心需求。当目标轨迹是直线、圆弧或复杂曲线时,仅通过离散的目标点无法实现平滑运动,这就需要插值运动(Interpolation Motion)技术 —— 通过控制算法在已知路径点之间计算出连续的中间点,使运动部件沿预定轨迹平滑移动。本…

GMT——用于人形全身控制的通用运动跟踪:两阶段师生训练框架下,全身基于单一策略,且自适应采样、MoE架构

前言 如此文《KungfuBot——基于物理约束和自适应运动追踪的人形全身控制PBHC&#xff0c;用于学习打拳或跳舞(即RL下的动作模仿和运控)》的开头所说 如此&#xff0c;便关注到最新出来的三个工作 第一个是GMT: General Motion Tracking for Humanoid Whole-Body Control第二个…

matlab版本粒子群算法(PSO)在路径规划中的应用

基于粒子群优化&#xff08;PSO&#xff09;算法的路径规划 MATLAB代码实现 1. 初始化环境和参数 % 初始化环境参数 mapSize [10, 10]; % 地图大小 startPoint [1, 1]; % 起点 endPoint [9, 9]; % 终点 obstacles [3, 3; 5, 5; 7, 7]; % 障碍物位置% PSO参数 numParticles …

Go语言面试:传值与传引用的区别及选择指南

在Go语言中&#xff0c;函数参数的传递方式有两种&#xff1a;传值&#xff08;pass-by-value&#xff09;和传引用&#xff08;pass-by-reference&#xff09;。理解这两种方式的区别及其适用场景&#xff0c;是成为Go语言开发高手的必备技能。本文将深入探讨Go语言中传值与传…

数据无言,网关有声 耐达讯自动化RS485转Profinet让千年液位数据“开口说话”

在能源行业的数字化转型浪潮中&#xff0c;你是否曾面临这样的困境&#xff1a; 现场大量采用RS485接口的液位计&#xff0c;数据孤立如信息孤岛&#xff0c;无法接入Profinet高速网络&#xff1f; 模拟信号传输距离受限&#xff0c;抗干扰能力弱&#xff0c;导致液位测量误差…

出口退税新政大提速:企业如何抓住政策红利,提升最高13%纯利?

近年来&#xff0c;出口退税政策的优化与升级&#xff0c;正在成为外贸企业提升资金周转率和利润率的关键。国家税务总局发布的 2022年第9号公告&#xff08;简称“9号公告”&#xff09;落地执行已两年&#xff0c;外贸行业普遍感受到退税速度显著加快&#xff0c;平均退税周期…

使用pytorch创建/训练/推理OCR模型

一、任务描述 从手写数字图像中自动识别出对应的数字&#xff08;0-9&#xff09;” 的问题&#xff0c;属于单标签图像分类任务&#xff08;每张图像仅对应一个类别&#xff0c;即 0-9 中的一个数字&#xff09; 1、任务的核心定义&#xff1a;输入与输出 输入&#xff1a;28…

新启航开启深孔测量新纪元:激光频率梳技术攻克光学遮挡,达 130mm 深度 2μm 精度

摘要&#xff1a;本文聚焦于深孔测量领域&#xff0c;介绍了一种创新的激光频率梳技术。该技术成功攻克传统测量中的光学遮挡难题&#xff0c;在深孔测量深度达 130mm 时&#xff0c;可实现 2μm 的高精度测量&#xff0c;为深孔测量开启了新的发展篇章。关键词&#xff1a;激光…