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.

193 lines
6.4 KiB

import sys
import socket
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QFileDialog, QLineEdit, QLabel, QComboBox
from PyQt5.QtCore import Qt
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Random import get_random_bytes
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.Util.Padding import pad
import hashlib
import os
class FileTransferApp(QWidget):
def __init__(self):
super().__init__()
# 生成公钥和私钥
self.generate_keys_if_not_exists()
self.init_ui()
def init_ui(self):
self.setWindowTitle('文件加密和传输')
self.setGeometry(100, 100, 400, 300)
layout = QVBoxLayout()
self.file_label = QLabel('未选择文件')
layout.addWidget(self.file_label)
self.select_button = QPushButton('选择文件')
self.select_button.clicked.connect(self.select_file)
layout.addWidget(self.select_button)
self.server_label = QLabel('服务器 IP:')
layout.addWidget(self.server_label)
self.server_ip = QLineEdit(self)
layout.addWidget(self.server_ip)
self.server_port_label = QLabel('服务器端口:')
layout.addWidget(self.server_port_label)
self.server_port = QLineEdit(self)
self.server_port.setText('59290') # 默认端口
layout.addWidget(self.server_port)
self.algorithm_label = QLabel('选择加密算法:')
layout.addWidget(self.algorithm_label)
self.algorithm_combo = QComboBox(self)
self.algorithm_combo.addItem('AES')
layout.addWidget(self.algorithm_combo)
self.mode_label = QLabel('选择加密模式:')
layout.addWidget(self.mode_label)
self.mode_combo = QComboBox(self)
self.mode_combo.addItem('CBC')
self.mode_combo.addItem('ECB')
layout.addWidget(self.mode_combo)
self.send_button = QPushButton('发送加密文件')
self.send_button.clicked.connect(self.send_file)
layout.addWidget(self.send_button)
self.setLayout(layout)
def select_file(self):
options = QFileDialog.Options()
filename, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "所有文件 (*);;文本文件 (*.txt)", options=options)
if filename:
self.file_label.setText(f'选择的文件: {filename}')
self.file_path = filename
def generate_keys_if_not_exists(self):
"""生成公钥和私钥,如果文件不存在"""
if not os.path.exists('public.pem') or not os.path.exists('private.pem'):
print("正在生成公钥和私钥...")
self.generate_keys()
else:
print("公钥和私钥已存在。")
def generate_keys(self):
"""生成公钥和私钥并保存为文件"""
key = RSA.generate(2048)
# 导出私钥
private_key = key.export_key()
with open('private.pem', 'wb') as f:
f.write(private_key)
print("私钥已保存为 'private.pem'")
# 导出公钥
public_key = key.publickey().export_key()
with open('public.pem', 'wb') as f:
f.write(public_key)
print("公钥已保存为 'public.pem'")
def load_public_key(self):
# 从文件加载接收方公钥(用于加密会话密钥)
with open('receiver_public.pem', 'rb') as f:
public_key = RSA.import_key(f.read())
return public_key
def load_private_key(self):
# 从文件加载私钥(用于签名文件)
with open('private.pem', 'rb') as f:
private_key = RSA.import_key(f.read())
return private_key
def encrypt_file(self, filename, public_key):
# 为 AES 加密生成随机会话密钥
session_key = get_random_bytes(16)
# 选择加密模式
mode = self.mode_combo.currentText()
if mode == 'CBC':
cipher_aes = AES.new(session_key, AES.MODE_CBC)
iv = cipher_aes.iv # 获取 CBC 模式使用的 IV
elif mode == 'ECB':
cipher_aes = AES.new(session_key, AES.MODE_ECB)
iv = None
# 加密文件
with open(filename, 'rb') as f:
plaintext = f.read()
ciphertext = cipher_aes.encrypt(pad(plaintext, AES.block_size)) # 对明文进行填充,以满足块大小要求
# 使用 RSA 加密会话密钥
cipher_rsa = PKCS1_OAEP.new(public_key)
encrypted_session_key = cipher_rsa.encrypt(session_key)
return encrypted_session_key, iv, ciphertext
def sign_file(self, filename, private_key):
# 计算文件的 SHA-256 哈希值
hash_obj = SHA256.new()
with open(filename, 'rb') as f:
while chunk := f.read(4096):
hash_obj.update(chunk)
# 使用私钥签名哈希值
signer = pkcs1_15.new(private_key)
signature = signer.sign(hash_obj)
return signature
def send_file(self):
# 获取服务器地址和端口
host = self.server_ip.text()
port = int(self.server_port.text())
# 加载公钥(用于加密会话密钥)
public_key = self.load_public_key()
# 加载私钥(用于签名文件)
private_key = self.load_private_key()
# 加密选择的文件
encrypted_session_key, iv, ciphertext = self.encrypt_file(self.file_path, public_key)
# 签名文件
signature = self.sign_file(self.file_path, private_key)
# 建立 socket 连接
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, port))
# 发送加密的会话密钥、IV、密文和签名
client_socket.sendall(encrypted_session_key)
client_socket.sendall(iv)
client_socket.sendall(ciphertext)
client_socket.sendall(signature)
client_socket.close()
print("文件发送成功。")
# 提示用户
self.file_label.setText('文件发送成功!')
def main():
app = QApplication(sys.argv)
ex = FileTransferApp()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()