深入解析P2P端到端加密聊天客户端:AES与XOR混合加密实现

本文详细分析了一个基于Python实现的P2P端到端加密聊天客户端,采用AES和XOR双重加密机制,支持安全消息传输和文件分享功能,包含完整的加密解密流程和网络通信实现。

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

  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
148
149
#!/usr/bin/python
# NSA P2P聊天客户端
# -*- 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()
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计