欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

基于Python开发简易局域网聊天工具

2025年07月28日 Python
在现代互联网环境中,隐私和安全越来越受到重视。端对端加密(end-to-end encryption, e2ee)技术可以确保只有通信的双方能够读取消息内容,即使是服务器也无法解密。本文将详细介绍如何

在现代互联网环境中,隐私和安全越来越受到重视。端对端加密(end-to-end encryption, e2ee)技术可以确保只有通信的双方能够读取消息内容,即使是服务器也无法解密。本文将详细介绍如何用python实现一个简单的局域网聊天工具,并为其添加端对端加密功能。

什么是端对端加密

端对端加密是一种通信加密方式,消息在发送端加密后,只有接收端能够解密。中间的任何节点(如路由器、服务器等)都只能看到加密后的数据,无法获取原始消息内容。这种加密方式广泛应用于即时通讯工具中,如whatsapp、signal等。

项目概述

本项目将实现以下功能:

  • 基于python的局域网聊天工具
  • 支持多客户端连接
  • 使用非对称加密(rsa)进行密钥交换
  • 使用对称加密(aes)加密通信内容
  • 简单的命令行界面

技术栈

编程语言:python 3.x

网络库:socket、threading

加密库:cryptography

其他:argparse(参数解析)

核心模块解析

网络通信基础

局域网聊天工具的核心是网络通信。python的socket库提供了低级别的网络接口,可以创建tcp或udp连接。本项目中采用tcp协议,因为它能保证消息的可靠传输。

import socket

# 创建tcp socket
server_socket = socket.socket(socket.af_inet, socket.sock_stream)

tcp通信需要明确的客户端和服务器端。服务器监听特定端口,客户端主动连接服务器。在聊天工具中,每个用户既是客户端(发送消息)又是服务器(接收消息)。

多线程处理

为了实现同时接收和发送消息,需要使用多线程。python的threading模块可以方便地创建和管理线程。

import threading

def receive_messages():
    while true:
        data = client_socket.recv(1024)
        print(f"received: {data.decode()}")

# 创建接收消息的线程
receive_thread = threading.thread(target=receive_messages)
receive_thread.start()

加密实现

加密部分分为两个阶段:密钥交换和消息加密。

密钥交换:使用rsa非对称加密。每个用户生成自己的rsa密钥对,公开公钥,保存私钥。当两个用户通信时,他们交换公钥,然后用对方的公钥加密一个随机的aes密钥(会话密钥)。

消息加密:使用aes对称加密。一旦会话密钥安全交换,后续通信都使用这个密钥进行加密解密,因为对称加密比非对称加密效率高很多。

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.ciphers import cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

# 生成rsa密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()

# 生成aes密钥
aes_key = os.urandom(32)  # 256-bit key

详细实现步骤

用户类设计

首先设计一个user类,包含用户的基本信息和加密相关操作。

class user:
    def __init__(self, name):
        self.name = name
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        self.public_key = self.private_key.public_key()
        self.peer_public_key = none
        self.aes_key = none
        
    def serialize_public_key(self):
        return self.public_key.public_bytes(
            encoding=serialization.encoding.pem,
            format=serialization.publicformat.subjectpublickeyinfo
        )
        
    def deserialize_public_key(self, key_bytes):
        self.peer_public_key = serialization.load_pem_public_key(
            key_bytes,
            backend=default_backend()
        )

密钥交换协议

设计一个简单的协议来交换公钥和会话密钥:

  • 连接建立后,双方交换rsa公钥
  • 一方生成aes密钥,用对方的公钥加密后发送
  • 对方收到后用私钥解密获取aes密钥
  • 后续通信使用aes加密
def perform_key_exchange(user, conn, is_initiator):
    # 交换公钥
    conn.sendall(user.serialize_public_key())
    peer_key_bytes = conn.recv(4096)
    user.deserialize_public_key(peer_key_bytes)
    
    if is_initiator:
        # 生成并发送aes密钥
        user.aes_key = os.urandom(32)
        encrypted_aes_key = user.peer_public_key.encrypt(
            user.aes_key,
            padding.oaep(
                mgf=padding.mgf1(algorithm=hashes.sha256()),
                algorithm=hashes.sha256(),
                label=none
            )
        )
        conn.sendall(encrypted_aes_key)
    else:
        # 接收并解密aes密钥
        encrypted_aes_key = conn.recv(4096)
        user.aes_key = user.private_key.decrypt(
            encrypted_aes_key,
            padding.oaep(
                mgf=padding.mgf1(algorithm=hashes.sha256()),
                algorithm=hashes.sha256(),
                label=none
            )
        )

消息加密解密

实现aes加密解密功能:

def encrypt_message(key, message):
    iv = os.urandom(16)  # 初始向量
    cipher = cipher(
        algorithms.aes(key),
        modes.cfb(iv),
        backend=default_backend()
    )
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message.encode()) + encryptor.finalize()
    return iv + ciphertext
    
def decrypt_message(key, ciphertext):
    iv = ciphertext[:16]
    cipher = cipher(
        algorithms.aes(key),
        modes.cfb(iv),
        backend=default_backend()
    )
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext[16:]) + decryptor.finalize()
    return plaintext.decode()

服务器和客户端实现

将上述功能整合到服务器和客户端代码中:

def start_server(port):
    user = user("server")
    with socket.socket(socket.af_inet, socket.sock_stream) as s:
        s.bind(('0.0.0.0', port))
        s.listen()
        conn, addr = s.accept()
        perform_key_exchange(user, conn, false)
        
        # 启动接收消息线程
        def receive():
            while true:
                data = conn.recv(4096)
                if not data: break
                message = decrypt_message(user.aes_key, data)
                print(f"received: {message}")
        
        threading.thread(target=receive, daemon=true).start()
        
        # 发送消息
        while true:
            message = input("> ")
            encrypted = encrypt_message(user.aes_key, message)
            conn.sendall(encrypted)

def start_client(host, port):
    user = user("client")
    with socket.socket(socket.af_inet, socket.sock_stream) as s:
        s.connect((host, port))
        perform_key_exchange(user, s, true)
        
        # 启动接收消息线程
        def receive():
            while true:
                data = s.recv(4096)
                if not data: break
                message = decrypt_message(user.aes_key, data)
                print(f"received: {message}")
        
        threading.thread(target=receive, daemon=true).start()
        
        # 发送消息
        while true:
            message = input("> ")
            encrypted = encrypt_message(user.aes_key, message)
            s.sendall(encrypted)

完整源代码

import socket
import threading
import argparse
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.ciphers import cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

class user:
    def __init__(self, name):
        self.name = name
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        self.public_key = self.private_key.public_key()
        self.peer_public_key = none
        self.aes_key = none
        
    def serialize_public_key(self):
        return self.public_key.public_bytes(
            encoding=serialization.encoding.pem,
            format=serialization.publicformat.subjectpublickeyinfo
        )
        
    def deserialize_public_key(self, key_bytes):
        self.peer_public_key = serialization.load_pem_public_key(
            key_bytes,
            backend=default_backend()
        )

def encrypt_message(key, message):
    iv = os.urandom(16)
    cipher = cipher(
        algorithms.aes(key),
        modes.cfb(iv),
        backend=default_backend()
    )
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message.encode()) + encryptor.finalize()
    return iv + ciphertext
    
def decrypt_message(key, ciphertext):
    iv = ciphertext[:16]
    cipher = cipher(
        algorithms.aes(key),
        modes.cfb(iv),
        backend=default_backend()
    )
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext[16:]) + decryptor.finalize()
    return plaintext.decode()

def perform_key_exchange(user, conn, is_initiator):
    conn.sendall(user.serialize_public_key())
    peer_key_bytes = conn.recv(4096)
    user.deserialize_public_key(peer_key_bytes)
    
    if is_initiator:
        user.aes_key = os.urandom(32)
        encrypted_aes_key = user.peer_public_key.encrypt(
            user.aes_key,
            padding.oaep(
                mgf=padding.mgf1(algorithm=hashes.sha256()),
                algorithm=hashes.sha256(),
                label=none
            )
        )
        conn.sendall(encrypted_aes_key)
    else:
        encrypted_aes_key = conn.recv(4096)
        user.aes_key = user.private_key.decrypt(
            encrypted_aes_key,
            padding.oaep(
                mgf=padding.mgf1(algorithm=hashes.sha256()),
                algorithm=hashes.sha256(),
                label=none
            )
        )

def start_server(port):
    user = user("server")
    with socket.socket(socket.af_inet, socket.sock_stream) as s:
        s.bind(('0.0.0.0', port))
        s.listen()
        print(f"server listening on port {port}")
        conn, addr = s.accept()
        print(f"connected by {addr}")
        perform_key_exchange(user, conn, false)
        
        def receive():
            while true:
                data = conn.recv(4096)
                if not data: break
                message = decrypt_message(user.aes_key, data)
                print(f"received: {message}")
        
        threading.thread(target=receive, daemon=true).start()
        
        while true:
            message = input("> ")
            encrypted = encrypt_message(user.aes_key, message)
            conn.sendall(encrypted)

def start_client(host, port):
    user = user("client")
    with socket.socket(socket.af_inet, socket.sock_stream) as s:
        s.connect((host, port))
        perform_key_exchange(user, s, true)
        
        def receive():
            while true:
                data = s.recv(4096)
                if not data: break
                message = decrypt_message(user.aes_key, data)
                print(f"received: {message}")
        
        threading.thread(target=receive, daemon=true).start()
        
        while true:
            message = input("> ")
            encrypted = encrypt_message(user.aes_key, message)
            s.sendall(encrypted)

if __name__ == "__main__":
    parser = argparse.argumentparser(description="secure lan chat")
    parser.add_argument("-s", "--server", action="store_true", help="run as server")
    parser.add_argument("-c", "--client", action="store_true", help="run as client")
    parser.add_argument("--host", type=str, default="localhost", help="server host")
    parser.add_argument("--port", type=int, default=12345, help="port number")
    args = parser.parse_args()
    
    if args.server:
        start_server(args.port)
    elif args.client:
        start_client(args.host, args.port)
    else:
        print("please specify --server or --client")

使用说明

在一台机器上运行服务器:

python chat.py --server --port 12345

在另一台机器上运行客户端(确保在同一局域网):

python chat.py --client --host <服务器ip> --port 12345

开始聊天,输入的消息会自动加密传输

安全注意事项

  • 本项目仅用于学习目的,不应用于生产环境
  • 实际应用中需要更完善的密钥管理和身份验证机制
  • 加密实现使用了cryptography库,这是python中比较可靠的加密库
  • 确保python环境是最新版本,避免已知的安全漏洞

通过这个项目,您可以学习到网络编程、多线程、加密算法等多项技术。

到此这篇关于基于python开发简易局域网聊天工具的文章就介绍到这了,更多相关python局域网聊天内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!