You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
7.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()