|
|
|
|
@ -0,0 +1,182 @@
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
from tkinter import ttk, messagebox, filedialog
|
|
|
|
|
import os
|
|
|
|
|
import base64
|
|
|
|
|
|
|
|
|
|
from .context import UIContext
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_crypto_tab(notebook: ttk.Notebook, ctx: UIContext, crypto_mod) -> ttk.Frame:
|
|
|
|
|
frame = ttk.Frame(notebook)
|
|
|
|
|
notebook.add(frame, text="加密/解密")
|
|
|
|
|
|
|
|
|
|
top = ttk.Frame(frame)
|
|
|
|
|
top.pack(fill=tk.X, padx=10, pady=10)
|
|
|
|
|
|
|
|
|
|
ttk.Button(top, text="选择待处理文件", command=ctx.choose_file).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
ttk.Label(top, text="算法:").pack(side=tk.LEFT)
|
|
|
|
|
alg_var = tk.StringVar(value="AES")
|
|
|
|
|
ttk.Combobox(top, textvariable=alg_var, values=["AES"], width=8, state="readonly").pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
ttk.Label(top, text="模式:").pack(side=tk.LEFT)
|
|
|
|
|
mode_var = tk.StringVar(value="GCM")
|
|
|
|
|
ttk.Combobox(top, textvariable=mode_var, values=["CBC", "GCM", "CTR"], width=8, state="readonly").pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
actions = ttk.Frame(frame)
|
|
|
|
|
actions.pack(fill=tk.X, padx=10, pady=6)
|
|
|
|
|
|
|
|
|
|
ttk.Button(actions, text="加密", command=lambda: _on_encrypt(ctx, crypto_mod, alg_var, mode_var)).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
ttk.Button(actions, text="解密", command=lambda: _on_decrypt(ctx, crypto_mod, alg_var, mode_var)).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _on_encrypt(ctx: UIContext, crypto_mod, alg_var: tk.StringVar, mode_var: tk.StringVar):
|
|
|
|
|
"""加密文件"""
|
|
|
|
|
if not ctx.ensure_impl(crypto_mod, "加密/解密"):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
selected = ctx.get_selected_file()
|
|
|
|
|
if not selected:
|
|
|
|
|
messagebox.showinfo("提示", "请先选择文件")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
algorithm = alg_var.get()
|
|
|
|
|
mode = mode_var.get()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 读取文件
|
|
|
|
|
with open(selected, 'rb') as f:
|
|
|
|
|
plaintext = f.read()
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[加密] 文件: {selected}, 大小: {len(plaintext)} bytes")
|
|
|
|
|
ctx.log(f"[加密] 算法: AES-256-{mode}")
|
|
|
|
|
|
|
|
|
|
# 生成随机密钥和IV
|
|
|
|
|
key = crypto_mod.generate_aes_key(256)
|
|
|
|
|
iv = crypto_mod.generate_iv(16)
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[加密] 生成密钥和IV...")
|
|
|
|
|
|
|
|
|
|
# 选择加密模式
|
|
|
|
|
if mode == 'CBC':
|
|
|
|
|
ciphertext = crypto_mod.aes_encrypt_cbc(plaintext, key, iv)
|
|
|
|
|
tag = None
|
|
|
|
|
elif mode == 'GCM':
|
|
|
|
|
ciphertext, tag = crypto_mod.aes_encrypt_gcm(plaintext, key, iv)
|
|
|
|
|
elif mode == 'CTR':
|
|
|
|
|
ciphertext = crypto_mod.aes_encrypt_ctr(plaintext, key, iv)
|
|
|
|
|
tag = None
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(f"未知的加密模式: {mode}")
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[加密] 加密完成,密文大小: {len(ciphertext)} bytes")
|
|
|
|
|
|
|
|
|
|
# 保存加密结果
|
|
|
|
|
output_path = filedialog.asksaveasfilename(
|
|
|
|
|
defaultextension=".enc",
|
|
|
|
|
filetypes=[("加密文件", "*.enc"), ("所有文件", "*.*")],
|
|
|
|
|
initialfile=os.path.basename(selected) + ".enc"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not output_path:
|
|
|
|
|
ctx.log("[加密] 用户取消保存")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 构建加密文件格式:mode(1字节) + key(32字节) + iv(16字节) + [tag(16字节)] + ciphertext
|
|
|
|
|
with open(output_path, 'wb') as f:
|
|
|
|
|
# 模式标识:CBC=1, GCM=2, CTR=3
|
|
|
|
|
mode_byte = {'CBC': b'\x01', 'GCM': b'\x02', 'CTR': b'\x03'}[mode]
|
|
|
|
|
f.write(mode_byte)
|
|
|
|
|
f.write(key)
|
|
|
|
|
f.write(iv)
|
|
|
|
|
if tag is not None:
|
|
|
|
|
f.write(tag)
|
|
|
|
|
f.write(ciphertext)
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[加密] 保存到: {output_path}")
|
|
|
|
|
messagebox.showinfo("加密成功", f"文件已加密并保存到:\n{output_path}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
import traceback
|
|
|
|
|
ctx.log(f"[加密] 失败: {e}")
|
|
|
|
|
ctx.log(traceback.format_exc())
|
|
|
|
|
messagebox.showerror("加密失败", str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _on_decrypt(ctx: UIContext, crypto_mod, alg_var: tk.StringVar, mode_var: tk.StringVar):
|
|
|
|
|
"""解密文件"""
|
|
|
|
|
if not ctx.ensure_impl(crypto_mod, "加密/解密"):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
selected = ctx.get_selected_file()
|
|
|
|
|
if not selected:
|
|
|
|
|
messagebox.showinfo("提示", "请先选择加密文件")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 读取加密文件
|
|
|
|
|
with open(selected, 'rb') as f:
|
|
|
|
|
data = f.read()
|
|
|
|
|
|
|
|
|
|
if len(data) < 50:
|
|
|
|
|
raise ValueError("文件太小,不是有效的加密文件")
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[解密] 文件: {selected}, 大小: {len(data)} bytes")
|
|
|
|
|
|
|
|
|
|
# 解析格式
|
|
|
|
|
mode_byte = data[0:1]
|
|
|
|
|
mode_map = {b'\x01': 'CBC', b'\x02': 'GCM', b'\x03': 'CTR'}
|
|
|
|
|
|
|
|
|
|
if mode_byte not in mode_map:
|
|
|
|
|
raise ValueError(f"未知的加密模式标识: {mode_byte.hex()}")
|
|
|
|
|
|
|
|
|
|
mode = mode_map[mode_byte]
|
|
|
|
|
ctx.log(f"[解密] 检测到模式: AES-256-{mode}")
|
|
|
|
|
|
|
|
|
|
key = data[1:33]
|
|
|
|
|
iv = data[33:49]
|
|
|
|
|
|
|
|
|
|
if mode == 'GCM':
|
|
|
|
|
tag = data[49:65]
|
|
|
|
|
ciphertext = data[65:]
|
|
|
|
|
else:
|
|
|
|
|
ciphertext = data[49:]
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[解密] 密钥长度: {len(key)} bytes, IV长度: {len(iv)} bytes")
|
|
|
|
|
|
|
|
|
|
# 解密
|
|
|
|
|
if mode == 'CBC':
|
|
|
|
|
plaintext = crypto_mod.aes_decrypt_cbc(ciphertext, key, iv)
|
|
|
|
|
elif mode == 'GCM':
|
|
|
|
|
plaintext = crypto_mod.aes_decrypt_gcm(ciphertext, key, iv, tag)
|
|
|
|
|
elif mode == 'CTR':
|
|
|
|
|
plaintext = crypto_mod.aes_decrypt_ctr(ciphertext, key, iv)
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(f"未知的解密模式: {mode}")
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[解密] 解密完成,明文大小: {len(plaintext)} bytes")
|
|
|
|
|
|
|
|
|
|
# 保存解密结果
|
|
|
|
|
output_path = filedialog.asksaveasfilename(
|
|
|
|
|
defaultextension="",
|
|
|
|
|
filetypes=[("所有文件", "*.*")],
|
|
|
|
|
initialfile=os.path.basename(selected).replace(".enc", "_decrypted")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not output_path:
|
|
|
|
|
ctx.log("[解密] 用户取消保存")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
with open(output_path, 'wb') as f:
|
|
|
|
|
f.write(plaintext)
|
|
|
|
|
|
|
|
|
|
ctx.log(f"[解密] 保存到: {output_path}")
|
|
|
|
|
messagebox.showinfo("解密成功", f"文件已解密并保存到:\n{output_path}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
import traceback
|
|
|
|
|
ctx.log(f"[解密] 失败: {e}")
|
|
|
|
|
ctx.log(traceback.format_exc())
|
|
|
|
|
messagebox.showerror("解密失败", str(e))
|