【问题标题】:How to handle ssl connections in raw Python socket?如何处理原始 Python 套接字中的 ssl 连接?
【发布时间】:2015-02-25 02:37:27
【问题描述】:

我正在编写一个程序来下载给定的网页。由于某些限制,我只需要对所有连接使用原始 python 套接字。因此,我与给定域(对象响应标头中的 Host 字段)建立了套接字连接,然后在此上发送 GET 请求。现在,当 url 是 https url 时,我想我需要先进行 SSL 握手(因为否则我会从服务器收到非 200 OK 响应和其他提到 P3P 策略的错误响应)。我检查了 curl 的响应以检查它是如何在我没有成功下载的情况下成功下载的,结果发现 curl 首先进行了 SSL 握手(这就是全部区别)。 curl 总是能够成功下载给定的对象,唯一的区别总是它所做的 SSL 握手。

所以我想知道如何在原始 python 套接字中进行 SSL 握手?基本上我想要一个简单的解决方案,除了使用原始套接字之外,它还允许我做最少的事情。

【问题讨论】:

  • docs.python.org/2/library/ssl.html,我猜他们希望您自己完成所有 HTTP,但允许您使用 SSL 库。
  • 您为什么要在纯 Python 中使用它的任何具体原因?我最近尝试解决同样的问题,最后只使用了wget。处理所有边缘情况,如 URL 重定向、cookie 和会话处理、SSL 握手等,还有递归爬取选项。
  • @miraculixx 这是在这个项目中指导我的人提供的限制。

标签: python sockets http ssl


【解决方案1】:

这是一个带有 SLL 的 TCP 客户端示例。

不确定这是否是下载网页的最佳方式,但它应该回答您的问题“原始 python 套接字中的 SSL 握手”。

您可能需要调整 struct.pack/unpack,但您大致了解:

import socket
import ssl
import struct
import binascii
import sys

class NotConnectedException(Exception):
    def __init__(self, message=None, node=None):
        self.message = message
        self.node = node

class DisconnectedException(Exception):
    def __init__(self, message=None, node=None):
        self.message = message
        self.node = node

class Connector:
    def __init__(self):
        pass

    def is_connected(self):
        return (self.sock and self.ssl_sock)

    def open(self, hostname, port, cacert):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.ssl_sock = ssl.wrap_socket(self.sock, ca_certs=cacert, cert_reqs=ssl.CERT_REQUIRED)

        if hostname == socket.gethostname():
            ipaddress = socket.gethostbyname_ex(hostname)[2][0]
            self.ssl_sock.connect((ipaddress, port))
        else:
            self.ssl_sock.connect((hostname, port))

        self.sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)

    def close(self):
        if self.sock: self.sock.close()
        self.sock = None
        self.ssl_sock = None

    def send(self, buffer):
        if not self.ssl_sock: raise NotConnectedException("Not connected (SSL Socket is null)")
        self.ssl_sock.sendall(struct.pack('L', len(buffer)))
        self.ssl_sock.sendall(buffer)

    def receive(self):
        if not self.ssl_sock: raise NotConnectedException("Not connected (SSL Socket is null)")
        data_size_buffer = self.ssl_sock.recv(4)

        if len(data_size_buffer) <= 0:
            raise DisconnectedException()

        data_size = struct.unpack('L', data_size_buffer)[0]
        received_size = 0
        data_buffer = ""

        while received_size < data_size:
            chunk = self.ssl_sock.recv(1024)
            data_buffer += chunk
            received_size += len(chunk)

        return data_buffer

然后你像这样使用这个类:

    connector = Connector.Connector()
    connector.open(server_ip, server_port, path_to_the_CA_cert.pem)
    connector.send(your_data)
    response = connector.receive()
    connector.close()

【讨论】:

    【解决方案2】:

    您可以使用 python ssl 模块的 wrap_socket 方法将您的套接字转换为与 SSL 通信的套接字。完成此操作后,您可以像往常一样使用它,但在内部,数据将为您加密和解密。这些是该方法的文档: https://docs.python.org/2/library/ssl.html#ssl.wrap_socket

    【讨论】:

      【解决方案3】:

      我认为更简单的方法是使用 SSL 上下文并包装 TCP 套接字。

      Python SSL 模块的文档通过示例给出了非常详尽的解释。我建议您阅读Python 2Python 3 ssl 模块文档的相关部分。应该很容易实现你想要的。

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-19
        • 1970-01-01
        • 1970-01-01
        • 2017-05-13
        • 2015-03-14
        • 2015-01-07
        • 1970-01-01
        相关资源
        最近更新 更多