parent
ba88ffe280
commit
51adade261
@ -0,0 +1,192 @@
|
|||||||
|
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()
|
Loading…
Reference in new issue