深入解析P2P端到端加密聊天客户端实现

本文详细分析了一个基于Python实现的P2P端到端加密聊天客户端,包含AES和XOR双重加密机制、文件传输功能以及UDP协议通信实现,完整展示了安全通信系统的技术架构。

实际P2P OTR客户端 - 技术分析与披露

代码实现概述

以下是一个完整的P2P加密聊天客户端Python实现,支持端到端加密通信和文件传输功能:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/python
# NSA P2P Chat Client
# -*- coding: utf-8 -*-
import sys
import socket
import base64
import random
import string
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
from threading import Thread
import getpass

class CryptoClient(object):
    def __init__(self):
        print("欢迎使用CryptoChat,一个安全的P2P聊天客户端")
        print("你想要加密吗?好的,开始吧!!!")
        self.IP = input("请输入你想要聊天的IP地址: ")
        self.PORT = int(input("输入通信端口: "))
        print()
        print("现在为不同的加密方法输入密钥,请确保它们不同。")
        print("出于安全考虑,密钥不会显示。")
        print()
        self.EncryptKeyXOR = getpass.getpass("输入XOR加密的密钥: ")
        self.EncryptKeyAES = hashlib.md5(getpass.getpass("输入AES的安全密码: ").encode()).hexdigest()
        print()
        input("当两个客户端都准备好时按回车。")
        ### AES填充相关设置
        BS = 16
        self.pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
        self.unpad = lambda s: s[:-ord(s[len(s) - 1:])]
        ### 启动聊天服务器和客户端
        try:
            Thread(target=self.RecvMSG, args=()).start()
        except socket.error as e:
            print(self.IP + " 尚未准备好!当 " + self.IP + " 准备好时按回车。")
            input()
            Thread(target=self.RecvMSG, args=()).start()
        self.SendMSG()

    def EncryptAES(self, raw):
        raw = self.pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.EncryptKeyAES.encode(), AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode())).decode()

    def DecryptAES(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.EncryptKeyAES.encode(), AES.MODE_CBC, iv)
        return self.unpad(cipher.decrypt(enc[16:])).decode()

    def XOR(self, securekey, data, mode):
        if mode == 1:
            securekey = securekey[::-1]
        temp = ''
        for char in securekey:
            for x in data:
                temp += chr(ord(x) ^ ord(char))
            data = temp
            temp = ''
        return data

    def EncryptMSG(self, data):
        data = self.XOR(self.EncryptKeyXOR, data, 0)
        data = self.EncryptAES(data)
        return data

    def DecryptMSG(self, data):
        data = self.DecryptAES(data)
        data = self.XOR(self.EncryptKeyXOR, data, 1)
        return data

    def SendMSG(self):
        clientsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP
        print()
        print("你现在正在与 '" + self.IP + "' 对话")
        while True:
            message = input("")
            if message.startswith("/send"):  # 发送文件命令
                self.SendFILE(message.split(" ")[1])
                continue
            if message == "/leave":
                message = self.EncryptMSG("\x03")
                clientsock.sendto(message.encode(), (self.IP, self.PORT))
                sys.exit(0)
            if message.startswith("/msg"):
                self.IP = message.split(" ")[1]
                print("[CLIENT] 你现在正在与 '" + self.IP + "' 对话")
                continue
            else:
                message = self.EncryptMSG("\x01" + message)
                clientsock.sendto(message.encode(), (self.IP, self.PORT))

    def SendFILE(self, file_):
        if file_.startswith(".") or file_.startswith("/"):  # 添加的安全措施
            print("[CLIENT] 出于安全和保护原因,不会发送以 '.' 或 '/' 开头的文件名。中止。")
        else:
            clientsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP
            data = "\x02" + file_ + "\xFF"
            with open(file_, "rb") as f:
                data += f.read()
            data = self.EncryptMSG(data)
            clientsock.sendto(data.encode(), (self.IP, self.PORT))
            print("[CLIENT] 文件已发送!")

    def RandStr(self, length):
        return ''.join(random.choice(string.ascii_letters) for _ in range(length))

    def RecvMSG(self):
        serversocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP
        serversocket.bind(('', self.PORT))
        while True:
            data, addr = serversocket.recvfrom(1073741824)  # 缓冲区大小为1GB(用于大文件/图像)
            data = self.DecryptMSG(data.decode())
            if data.startswith("\x02"):
                filename = ''
                data = list(data)
                del data[0]
                for i in data:
                    if i == "\xFF":
                        break
                    else:
                        filename += i
                del data[0]  # 删除协议字符
                del data[:len(filename)]  # 删除文件结束字符
                if filename.startswith(".") or filename.startswith("/"):  # 尝试利用!
                    print("[!!!警报!!!] " + addr[0] + " 试图覆盖你的 " + filename)
                else:
                    print("[CLIENT] " + addr[0] + " 发送了 " + filename)
                    print("[CLIENT] 正在下载...")  # 下载那个东西
                    data = ''.join(data)
                    with open(filename, "wb") as f:
                        f.write(data.encode())
                    print("[CLIENT] 已保存。")
            elif data.startswith("\x01"):  # 所有消息以 "\x01" 开头以防止文件垃圾邮件
                data = list(data)
                del data[0]
                data = ''.join(data)
                print("[" + addr[0] + "] >  |   " + data)
            elif data.startswith("\x03"):
                print("[CLIENT] " + addr[0] + " 已离开。")
                sys.exit(0)

if __name__ == "__main__":
    CryptoClient()

技术特性

加密机制

  • 双重加密层: 结合XOR和AES-CBC模式加密
  • 密钥管理: 使用MD5哈希处理AES密码
  • 安全输入: 使用getpass模块隐藏密钥输入

网络通信

  • UDP协议: 使用无连接UDP套接字进行通信
  • 多线程处理: 独立的发送和接收线程
  • 大文件支持: 1GB缓冲区支持大文件传输

安全特性

  • 文件传输安全: 防止路径遍历攻击
  • 协议标识: 使用特殊字符标识消息类型
  • 异常处理: 完善的错误处理和连接恢复机制

协议设计

消息使用特殊前缀标识类型:

  • \x01: 普通聊天消息
  • \x02: 文件传输
  • \x03: 离开通知

文件传输格式:\x02 + 文件名 + \xFF + 文件内容

该实现展示了完整的P2P加密通信系统,包括密钥交换、加密解密、文件传输和网络安全防护措施。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计