使用SOCKET技术实现数据交换,全部加解密操作都在客户端完成,服务端不参与数据处理,仅供数据交换。
使用方法
安装服务端与客户端依赖的支持库。运行一个服务端与两个客户端。
语言
Python
效果图
上面为服务端,下面两个为客户端
bd4939a6c83c4aa785e97a667f286d4e.png (144.21 KB, 下载次数: 0)
下载附件
2024-10-17 13:21 上传
代码
server.py
import socket
import threading
from datetime import datetime
from cryptography.fernet import Fernet
# 生成服务端的随机密钥
server_key = Fernet.generate_key()
clients = {}
lock = threading.Lock() # 用于多线程访问的锁
def handle_client(client_socket, client_address, client_id):
print(f"客户端 {client_address} 已连接,ID: {client_id}")
# 发送服务端密钥给客户端
client_socket.sendall(server_key)
# 接收客户端密钥混合数据
client_key_mixed = client_socket.recv(1024)
with lock:
clients[client_id] = {"socket": client_socket, "key_mixed": client_key_mixed}
# 如果两个客户端都已连接,进行密钥交换
if len(clients) == 2:
other_client_id = 2 if client_id == 1 else 1
other_client = clients[other_client_id]
# 将 client_id 的混合密钥发送给 other_client
other_client["socket"].sendall(client_key_mixed)
# 将 other_client 的混合密钥发送给 client_id
client_socket.sendall(other_client["key_mixed"])
# 处理加密消息的交换
while True:
try:
# 接收客户端加密消息并转发
encrypted_message = client_socket.recv(1024)
if encrypted_message:
# 获取当前时间
exchange_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 输出接收到的密文及时间
print(f"[{exchange_time}] 从客户端 {client_id} 接收到的密文: {encrypted_message}")
# 转发消息给另一个客户端
other_client_socket = clients[2 if client_id == 1 else 1]["socket"]
other_client_socket.sendall(encrypted_message)
# 输出转发操作
print(f"[{exchange_time}] 密文转发给另一个客户端")
else:
break
except Exception as e:
print(f"处理客户端 {client_id} 消息时出错: {e}")
break
print(f"客户端 {client_address} 已断开")
client_socket.close()
def start_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 65432))
server.listen(2)
print("服务器启动,等待客户端连接...")
client_id = 1
while True:
client_socket, client_address = server.accept()
# 为每个客户端启动新线程
client_handler = threading.Thread(target=handle_client, args=(client_socket, client_address, client_id))
client_handler.start()
client_id += 1
start_server()
client.py
import socket
import threading
import base64
from cryptography.fernet import Fernet
import os
# 生成客户端的随机密钥
client_key = Fernet.generate_key()
# 全局变量存储聊天记录
chat_history = []
# 全局变量存储混合后的密钥
final_key_base64 = None
def xor_bytes(byte1, byte2):
# 使用 XOR 混合两个字节串,并确保两者的长度一致
return bytes(a ^ b for a, b in zip(byte1, byte2))
def clear_screen():
"""清空屏幕并重新绘制聊天界面"""
os.system('cls') # 对于 Windows,使用 `cls` 清屏
# 显示混合后的密钥
if final_key_base64:
print(f"共享密钥: {final_key_base64.decode()}\n")
# 只显示最新的10条聊天记录
for message in chat_history[-10:]:
print(message)
# 重新显示输入提示
print("请输入要发送的消息:", end='', flush=True)
def receive_messages(client_socket, fernet):
"""用于接收消息的线程"""
while True:
try:
encrypted_response = client_socket.recv(1024)
if encrypted_response:
# 解密消息
decrypted_message = fernet.decrypt(encrypted_response).decode('utf-8')
# 将收到的消息添加到聊天记录
chat_history.append(f"收到消息: {decrypted_message}")
# 清空屏幕并重新显示聊天记录和输入提示
clear_screen()
except Exception as e:
print(f"接收消息时出错: {e}")
break
def send_messages(client_socket, fernet):
"""用于发送消息的主线程"""
while True:
message = input()
if message:
# 加密消息
encrypted_message = fernet.encrypt(message.encode('utf-8'))
# 发送加密消息
client_socket.sendall(encrypted_message)
# 将发送的消息添加到聊天记录
chat_history.append(f"我: {message}")
# 清空屏幕并重新显示聊天记录和输入提示
clear_screen()
def start_client():
global final_key_base64
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 65432)
client_socket.connect(server_address)
# 接收服务端的密钥
server_key = client_socket.recv(1024)
# 生成客户端的密钥并与服务器的密钥混合
client_key_mixed = xor_bytes(client_key, server_key)
print(f"混合后的客户端密钥发送给服务器")
# 发送混合后的密钥给服务器
client_socket.sendall(client_key_mixed)
# 接收对方客户端的混合密钥
other_client_key_mixed = client_socket.recv(1024)
# 将对方的密钥与自己的密钥混合,得到共享密钥
shared_key = xor_bytes(other_client_key_mixed, client_key)
# 取共享密钥的最后32个字节作为Fernet密钥
final_key = shared_key[-32:]
final_key_base64 = base64.urlsafe_b64encode(final_key)
print(f"共享密钥生成: {final_key_base64.decode()}")
# 使用最终的共享密钥进行加密与解密
fernet = Fernet(final_key_base64)
# 清空屏幕,准备显示聊天界面
clear_screen()
# 创建一个线程来接收消息
receive_thread = threading.Thread(target=receive_messages, args=(client_socket, fernet))
receive_thread.daemon = True # 让线程在主程序退出时结束
receive_thread.start()
# 主线程负责发送消息
send_messages(client_socket, fernet)
start_client()
PS
本来是想做一个多人的端对端,但是不太行。我的思路就是通过混合多个客户端密钥获得公共广播密钥用来加解密,but太难了。