Python带登录的Fernet加密聊天室客户端解密失败求助
问题分析与解决方案
你的聊天室客户端无法解密消息,核心是服务器端的两个逻辑错误导致加密数据损坏或双重加密:
错误点1:服务器错误地处理客户端发送的加密数据
客户端发送的是加密后的二进制数据,但服务器接收时先尝试将其解码为UTF-8字符串,再重新编码。加密后的bytes并非合法的UTF-8序列,这一步会直接损坏原始加密数据,后续解密即使不报错,也无法得到正确内容。
错误点2:广播时的双重加密逻辑
服务器在接收并解密客户端消息后,先重新加密明文,然后广播时又将这个加密后的消息与发送者前缀拼接再加密一次,导致客户端收到双重加密的数据,一次解密无法得到明文。
修正后的服务器代码
import socket import threading from cryptography.fernet import Fernet # Generate a secret key for encryption key = Fernet.generate_key() cipher_suite = Fernet(key) print(key.decode()) # Store username-password pairs (for demo purposes) user_credentials = { "user1": "password1", "user2": "password2", } # Store connected clients connected_clients = {} def handle_client(client_socket): username = client_socket.recv(1024).decode('utf-8') password = client_socket.recv(1024).decode('utf-8') if username in user_credentials and user_credentials[username] == password: client_socket.send("Authenticated".encode('utf-8')) print(f"{username} has connected.") connected_clients[username] = client_socket while True: try: # 直接接收加密的二进制数据,不做UTF-8解码 message = client_socket.recv(1024) # 先判断连接是否断开 if not message: del connected_clients[username] client_socket.close() print(f"{username} has disconnected.") break # 直接解密二进制数据 decrypted_message = cipher_suite.decrypt(message) decoded_message = decrypted_message.decode('utf-8') print(f"Received from {username}: {decoded_message}") # 将明文传给广播函数,由广播函数负责加密带前缀的消息 broadcast_message(username, decoded_message) except Exception as e: print(str(e)) break else: client_socket.send("Authentication failed".encode('utf-8')) client_socket.close() def broadcast_message(sender, plain_message): for username, client_socket in connected_clients.items(): if username != sender: try: # 将发送者和明文拼接后加密,生成最终要发送的加密消息 encrypted_message = cipher_suite.encrypt(f"{sender}: {plain_message}".encode('utf-8')) client_socket.send(encrypted_message) except Exception as e: print(str(e)) continue def main(): PORT = 5052 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(("0.0.0.0", PORT)) server.listen(5) print(f"Server listening on port {PORT}") while True: client_socket, addr = server.accept() client_handler = threading.Thread(target=handle_client, args=(client_socket,)) client_handler.start() if __name__ == "__main__": main()
优化后的客户端代码(非必须但更高效)
import socket from cryptography.fernet import Fernet import threading # 提前创建Fernet实例,避免每次发送/接收时重复初始化 key = input("Enter the secret key: ").encode() cipher_suite = Fernet(key) def send_message(client_socket): while True: message = input() client_socket.send(cipher_suite.encrypt(message.encode())) def receive_message(client_socket): while True: try: message = client_socket.recv(1024) decrypted_message = cipher_suite.decrypt(message) print(decrypted_message.decode('utf-8')) except Exception as e: print(str(e)) break def main(): PORT = 5052 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("YOUR_SERVER_IP", PORT)) username = input("Username: ") password = input("Password: ") client.send(username.encode()) client.send(password.encode()) response = client.recv(1024).decode() if response == "Authenticated": print("Authentication successful. You are now connected to the chatroom.") send_thread = threading.Thread(target=send_message, args=(client,)) receive_thread = threading.Thread(target=receive_message, args=(client,)) send_thread.start() receive_thread.start() else: print("Authentication failed. Closing connection.") client.close() if __name__ == "__main__": main()
额外注意事项
- 确保客户端输入的密钥与服务器启动时打印的密钥完全一致,不要包含多余的空格或换行符。
- 服务器绑定的IP改为
0.0.0.0可以允许外部设备连接,客户端需替换YOUR_SERVER_IP为服务器实际IP。 - 测试时先启动服务器,复制打印的密钥,再启动客户端输入该密钥。
内容的提问来源于stack exchange,提问作者7ven Perks
相关产品推荐
相关产品推荐

