|
|
from Crypto.PublicKey import RSA
|
|
|
from Crypto.Cipher import AES, PKCS1_OAEP, DES3
|
|
|
from Crypto.Hash import SHA256
|
|
|
from Crypto.Signature import pkcs1_15
|
|
|
from Crypto.Random import get_random_bytes
|
|
|
from Crypto.Util.Padding import pad, unpad
|
|
|
import base64
|
|
|
import json
|
|
|
import tkinter as tk
|
|
|
from tkinter import filedialog, messagebox
|
|
|
from tkinter.ttk import Combobox
|
|
|
import requests
|
|
|
|
|
|
def generate_rsa_keys():
|
|
|
"""
|
|
|
生成RSA密钥对并保存到文件中。
|
|
|
"""
|
|
|
# 生成RSA密钥对
|
|
|
key = RSA.generate(2048)
|
|
|
|
|
|
# 导出私钥为PEM格式
|
|
|
private_key = key.export_key()
|
|
|
with open('sender_private.pem', 'wb') as f:
|
|
|
f.write(private_key)
|
|
|
|
|
|
# 导出公钥为PEM格式
|
|
|
public_key = key.publickey().export_key()
|
|
|
with open('recipient_public.pem', 'wb') as f:
|
|
|
f.write(public_key)
|
|
|
|
|
|
def encrypt_file(file_path, recipient_public_key_path, symmetric_algorithm='AES', mode='CBC'):
|
|
|
"""
|
|
|
使用对称密钥加密文件,并使用接收方的公钥将对称密钥封装在数字信封中。
|
|
|
|
|
|
:param file_path: 要加密的文件路径。
|
|
|
:param recipient_public_key_path: 接收方公钥文件路径。
|
|
|
:param symmetric_algorithm: 对称加密算法 ('AES' 或 'DES3')。
|
|
|
:param mode: 对称算法的加密模式 ('ECB' 或 'CBC')。
|
|
|
:return: 包含加密对称密钥、IV(如果使用CBC模式)、加密文件数据和签名的数字信封。
|
|
|
"""
|
|
|
# 读取要加密的文件
|
|
|
with open(file_path, 'rb') as f:
|
|
|
file_data = f.read()
|
|
|
|
|
|
# 计算文件摘要
|
|
|
hash_obj = SHA256.new(file_data)
|
|
|
digest = hash_obj.digest()
|
|
|
|
|
|
# 使用发送方的私钥对摘要进行签名
|
|
|
with open('sender_private.pem', 'r') as f:
|
|
|
sender_private_key = RSA.import_key(f.read())
|
|
|
signature = pkcs1_15.new(sender_private_key).sign(hash_obj)
|
|
|
|
|
|
# 生成随机对称密钥
|
|
|
if symmetric_algorithm == 'AES':
|
|
|
symmetric_key = get_random_bytes(16) # AES-128
|
|
|
elif symmetric_algorithm == 'DES3':
|
|
|
symmetric_key = get_random_bytes(24) # Triple DES
|
|
|
else:
|
|
|
raise ValueError("不支持的算法")
|
|
|
|
|
|
# 使用对称密钥加密文件
|
|
|
if symmetric_algorithm == 'AES':
|
|
|
if mode == 'ECB':
|
|
|
cipher_aes = AES.new(symmetric_key, AES.MODE_ECB)
|
|
|
padded_file_data = pad(file_data, AES.block_size)
|
|
|
encrypted_file_data = cipher_aes.encrypt(padded_file_data)
|
|
|
elif mode == 'CBC':
|
|
|
iv = get_random_bytes(AES.block_size)
|
|
|
cipher_aes = AES.new(symmetric_key, AES.MODE_CBC, iv)
|
|
|
padded_file_data = pad(file_data, AES.block_size)
|
|
|
encrypted_file_data = cipher_aes.encrypt(padded_file_data)
|
|
|
else:
|
|
|
raise ValueError("不支持的模式")
|
|
|
|
|
|
elif symmetric_algorithm == 'DES3':
|
|
|
if mode == 'ECB':
|
|
|
cipher_des3 = DES3.new(symmetric_key, DES3.MODE_ECB)
|
|
|
padded_file_data = pad(file_data, DES3.block_size)
|
|
|
encrypted_file_data = cipher_des3.encrypt(padded_file_data)
|
|
|
elif mode == 'CBC':
|
|
|
iv = get_random_bytes(DES3.block_size)
|
|
|
cipher_des3 = DES3.new(symmetric_key, DES3.MODE_CBC, iv)
|
|
|
padded_file_data = pad(file_data, DES3.block_size)
|
|
|
encrypted_file_data = cipher_des3.encrypt(padded_file_data)
|
|
|
else:
|
|
|
raise ValueError("不支持的模式")
|
|
|
|
|
|
else:
|
|
|
raise ValueError("不支持的算法")
|
|
|
|
|
|
# 读取接收方的公钥
|
|
|
with open(recipient_public_key_path, 'r') as f:
|
|
|
recipient_public_key = RSA.import_key(f.read())
|
|
|
|
|
|
# 使用接收方的公钥加密对称密钥
|
|
|
cipher_rsa = PKCS1_OAEP.new(recipient_public_key)
|
|
|
encrypted_symmetric_key = cipher_rsa.encrypt(symmetric_key)
|
|
|
|
|
|
# 创建数字信封
|
|
|
digital_envelope = {
|
|
|
'encrypted_symmetric_key': base64.b64encode(encrypted_symmetric_key).decode('utf-8'),
|
|
|
'iv': base64.b64encode(iv).decode('utf-8') if mode == 'CBC' else None,
|
|
|
'encrypted_file_data': base64.b64encode(encrypted_file_data).decode('utf-8'),
|
|
|
'signature': base64.b64encode(signature).decode('utf-8')
|
|
|
}
|
|
|
|
|
|
return digital_envelope
|
|
|
|
|
|
def select_file():
|
|
|
root = tk.Tk()
|
|
|
root.withdraw() # 隐藏主窗口
|
|
|
file_path = filedialog.askopenfilename(title="选择要加密的文件")
|
|
|
return file_path
|
|
|
|
|
|
def send_digital_envelope(digital_envelope, server_url):
|
|
|
try:
|
|
|
response = requests.post(server_url, json=digital_envelope)
|
|
|
response.raise_for_status()
|
|
|
result = response.json()
|
|
|
if result['status'] == 'success':
|
|
|
messagebox.showinfo("成功", "数字信封已成功发送")
|
|
|
else:
|
|
|
messagebox.showerror("错误", "发送失败")
|
|
|
except requests.exceptions.RequestException as e:
|
|
|
messagebox.showerror("错误", str(e))
|
|
|
|
|
|
def on_encrypt_button_click():
|
|
|
file_path = file_entry.get()
|
|
|
if not file_path:
|
|
|
messagebox.showwarning("警告", "请选择一个文件")
|
|
|
return
|
|
|
|
|
|
symmetric_algorithm = algorithm_combobox.get()
|
|
|
mode = mode_combobox.get()
|
|
|
|
|
|
if not symmetric_algorithm or not mode:
|
|
|
messagebox.showwarning("警告", "请选择对称加密算法和模式")
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
digital_envelope = encrypt_file(file_path, 'recipient_public.pem', symmetric_algorithm, mode)
|
|
|
send_digital_envelope(digital_envelope, server_url_entry.get())
|
|
|
except Exception as e:
|
|
|
messagebox.showerror("错误", str(e))
|
|
|
|
|
|
def main():
|
|
|
global file_entry, algorithm_combobox, mode_combobox, server_url_entry
|
|
|
|
|
|
root = tk.Tk()
|
|
|
root.title("文件加密工具")
|
|
|
|
|
|
# 文件选择部分
|
|
|
file_label = tk.Label(root, text="选择文件:")
|
|
|
file_label.grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
|
|
|
|
|
|
file_entry = tk.Entry(root, width=50)
|
|
|
file_entry.grid(row=0, column=1, padx=10, pady=10)
|
|
|
|
|
|
browse_button = tk.Button(root, text="浏览", command=lambda: file_entry.insert(0, select_file()))
|
|
|
browse_button.grid(row=0, column=2, padx=10, pady=10)
|
|
|
|
|
|
# 对称加密算法选择部分
|
|
|
algorithm_label = tk.Label(root, text="选择对称加密算法:")
|
|
|
algorithm_label.grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
|
|
|
|
|
|
algorithm_combobox = Combobox(root, values=['AES', 'DES3'], state='readonly')
|
|
|
algorithm_combobox.set('AES')
|
|
|
algorithm_combobox.grid(row=1, column=1, padx=10, pady=10)
|
|
|
|
|
|
# 加密模式选择部分
|
|
|
mode_label = tk.Label(root, text="选择加密模式:")
|
|
|
mode_label.grid(row=2, column=0, padx=10, pady=10, sticky=tk.W)
|
|
|
|
|
|
mode_combobox = Combobox(root, values=['ECB', 'CBC'], state='readonly')
|
|
|
mode_combobox.set('CBC')
|
|
|
mode_combobox.grid(row=2, column=1, padx=10, pady=10)
|
|
|
|
|
|
# 服务器URL输入部分
|
|
|
server_url_label = tk.Label(root, text="服务器URL:")
|
|
|
server_url_label.grid(row=3, column=0, padx=10, pady=10, sticky=tk.W)
|
|
|
|
|
|
server_url_entry = tk.Entry(root, width=50)
|
|
|
server_url_entry.grid(row=3, column=1, padx=10, pady=10)
|
|
|
server_url_entry.insert(0, "http://localhost:8000")
|
|
|
|
|
|
# 加密按钮
|
|
|
encrypt_button = tk.Button(root, text="加密并发送", command=on_encrypt_button_click)
|
|
|
encrypt_button.grid(row=4, columnspan=3, pady=20)
|
|
|
|
|
|
root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
# 生成RSA密钥对
|
|
|
generate_rsa_keys()
|
|
|
|
|
|
# 启动GUI应用程序
|
|
|
main()
|
|
|
|
|
|
|
|
|
|