python写的代理对socket通讯加密出错
正在学习写的网页代理服务器在对中间数据加密时出现错误,浏览器接收资源不全,
用的 SocketServer 模块,
client中:
python
#!/usr/bin/env python2 # conding=utf-8 import sys import socket from SocketServer import ThreadingMixIn, TCPServer, StreamRequestHandler import struct import select import logging from AesEncrypt import AesEncrypt import hashlib def encrypt(data): newencrypt = AesEncrypt() psw = 'admin' salt = '123456' newencrypt.md5(psw,salt) return newencrypt.encrypt(data) def decrypt(data): newdecrypt = AesEncrypt() psw = 'admin' salt = '123456' newdecrypt.md5(psw,salt) return newdecrypt.decrypt(data) def sendall(sock,data): length = 0 while True: result = sock.send(data[length:]) if result < 0: return result length += result if length == len(data): return length class Log(): def __init__(self): self.logger = logging.getLogger('log') self.logger.setLevel(logging.DEBUG) f = logging.FileHandler('./log') f.setLevel(logging.DEBUG) self.logger.addHandler(f) def log(self, data): self.logger.info(data) class Server(ThreadingMixIn,TCPServer):pass class Socks5Server(StreamRequestHandler): def handleData(self, source, dest): try: while 1: active, w, x= select.select([source, dest], [], [], 5) if not active: break if source in active: data = source.recv(4096) if len(data) <= 0: break data = encrypt(data) re = sendall(dest,data) #re = dest.send(data) if re < len(data): raise Exception('Failed to send all data') if dest in active: data = dest.recv(4096) if len(data) <= 0: break data = decrypt(data) re = sendall(source,data) #re = source.send(data) if re < len(data): raise Exception('Failed to send all data') finally: self.log.log('closed') source.close() dest.close() def handle(self): self.log = Log() socks = self.connection socks.recv(262) socks.send("\x05\x00") data = self.rfile.read(4) #print data mode = ord(data[1]) if mode !=1: logging.warn('mode !=1') return addrtypr = ord(data[3]) addr_to_send = data[3] if addrtypr == 1: addr_ip = self.rfile.read(4) addr = socket.inet_ntoa(addr_ip) addr_to_send += addr_ip elif addrtypr == 3: addr_len = self.rfile.read(1) addr = self.rfile.read(ord(addr_len)) addr_to_send += addr_len +addr else: logging.warn('addr_type not support') return addr_port = self.rfile.read(2) addr_to_send += addr_port port = struct.unpack('>H', addr_port) reply = "\x05\x00\x00\x01" reply += socket.inet_aton('0.0.0.0') + struct.pack('>H', 2222) self.wfile.write(reply) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1) sock.connect(('127.0.0.1', 1083)) sock.send(addr_to_send) self.handleData(socks, sock) logging.info('connecting %s:%s'%(addr,port[0])) if __name__=="__main__": HOST, PORT="localhost", 8088 try: server = Server((HOST, PORT), Socks5Server) server.serve_forever() except KeyboardInterrupt: server.shutdown() sys.exit()
server中:
python
#!/user/bin/env python2 # conding=utf-8 from AesEncrypt import AesEncrypt from SocketServer import StreamRequestHandler, ThreadingMixIn, TCPServer import socket import struct from select import select import logging import hashlib def encrypt(data): newencrypt = AesEncrypt() psw = 'admin' salt = '123456' newencrypt.md5(psw,salt) return newencrypt.encrypt(data) def decrypt(data): newdecrypt = AesEncrypt() psw = 'admin' salt = '123456' newdecrypt.md5(psw,salt) return newdecrypt.decrypt(data) def sendall(sock,data): length = 0 while True: result = sock.send(data[length:]) if result <0: return result length += result if length == len(data): return length class ServerThread(ThreadingMixIn, TCPServer): pass class Server(StreamRequestHandler): def handle(self): socks = self.connection addrtype = ord(socks.recv(1)) if addrtype == 1: addr = socket.inet_ntoa(self.rfile.read(4)) elif addrtype == 3: addr = self.rfile.read(ord(socks.recv(1))) else: logging.warn('addr type not support') return port = struct.unpack('>H', self.rfile.read(2)) logging.info('connecting %s:%d'%(addr,port[0])) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,1) client.connect((addr, port[0])) try: while 1: active, w, x = select([socks, client], [], []) if not active: break if socks in active: data = socks.recv(4096) if len(data)<= 0: break data = decrypt(data) re = sendall(client,data) #re = client.send(data) if re < len(data): raise Exception('Failed to send all data') if client in active: data = client.recv(4096) if len(data) <= 0: break data = encrypt(data) re = sendall(socks,data) #re = socks.send(data) #undata = decrypt(data) if re < len(data): raise Exception('Failed to send all data') finally: print 'closed!!!' client.close() socks.close() if __name__== "__main__": HOST, PORT="localhost", 1083 server = ServerThread((HOST, PORT), Server) server.serve_forever()
AesEncrypt.py:
python
# coding=utf-8 from Crypto.Cipher import AES from binascii import b2a_hex, a2b_hex import hashlib class AesEncrypt(object): def md5(self, psw, salt): self.hasli = salt + psw self.MD5 = hashlib.md5(self.hasli).hexdigest() self.KEY = self.MD5[0:16] self.IV = self.MD5[16:] def encrypt(self, data): length = 16 count = len(data) if count == 0: return data elif count < length: add = (length - count) data = data + ('$' * add) elif count > length: if count % length == 0: data = data else: add = (length - (count % length)) data = data + ('$' * add) obj = AES.new(self.KEY, AES.MODE_CBC, self.IV) cipherdata = obj.encrypt(data) #cipherdata = b2a_hex(cipherdata) return cipherdata def decrypt(self, cipherdata): if len(cipherdata) == 0: return cipherdata else: obj = AES.new(self.KEY, AES.MODE_CBC, self.IV) #data = a2b_hex(cipherdata) data = obj.decrypt(cipherdata) data = data.rstrip('$') return data
encrypt()是加密函数,decrypt()解密,用的是AES加密,因为不足16位倍数时需要补位,但解密后会删掉补位的字符。
开始代理后,结果网页加载不全,一般只能打开一部分资源,大部分加载不出来(如不能获取css,js,图片等等,但都是随机会少一些资源。)。
因为对TCP协议不是很了解,用wireshark抓包分析也看不出什么,
比较收到、发出的数据包的md5值也是一样,不知道哪出了问题,新人求指点啊~~~
sockcs.sendall() 也是使用过,有时候会爆出error: [Errno 104] Connection reset by peer
Lua2z
10 years, 8 months ago
Answers
代码看完了,结论是:你不懂加密。
- 你用了 CBC 模式,但是每次均使用新的实例
- 你的 IV 从来不变化。这样还不如用 ECB 模式。反正早不安全了不是么
你这样做的结果除了不安全之外,你必须 保证每次加密和对应的解密的数据是同一段数据 ,但是你没有做这种保证。经常会出现 你加密了4096字节数据发过去,对方收到了2048字节就去解密,后边的2048字节使用新初始化的对象进行解密 。所以数据才会不正确。
写网络程序的时候要注意 不完整的 recv 和 send(实际处理的数据量小于预期) 。而写加密程序的时候要注意 你 很可能 根本不懂 加密 (这里有四个链接哦)。
建议使用现成的 TLS/SSL。
MJ蓝猫党
answered 10 years, 8 months ago