尝试从 requests 中的响应获取SSL证书。

什么是这样做的好方法?

最佳答案

requests故意包装了这样的低级内容。通常,您唯一要做的就是verify that the certs are valid。为此,只需传递verify=True即可。如果要使用非标准的cacert bundle 包,也可以通过。例如:

resp = requests.get('https://example.com', verify=True, cert=['/path/to/my/ca.crt'])

此外,requests主要是其他库的一组包装,主要是 urllib3 和stdlib的 http.client (或对于2.x来说是httplib)和 ssl

有时,答案只是到达较低级别的对象(例如resp.rawurllib3.response.HTTPResponse),但是在许多情况下这是不可能的。

这就是其中一种情况。唯一可以看到证书的对象是http.client.HTTPSConnection(或urllib3.connectionpool.VerifiedHTTPSConnection,但这只是前者的子类)和ssl.SSLSocket,当请求返回时,这些对象都不存在。 (正如connectionpool的名称所暗示的,HTTPSConnection对象存储在池中,并且可以在完成后立即重用; SSLSocketHTTPSConnection的成员。)

因此,您需要打补丁,以便可以将数据复制到整个链中。可能就这么简单:
HTTPResponse = requests.packages.urllib3.response.HTTPResponse
orig_HTTPResponse__init__ = HTTPResponse.__init__
def new_HTTPResponse__init__(self, *args, **kwargs):
    orig_HTTPResponse__init__(self, *args, **kwargs)
    try:
        self.peercert = self._connection.sock.getpeercert()
    except AttributeError:
        pass
HTTPResponse.__init__ = new_HTTPResponse__init__

HTTPAdapter = requests.adapters.HTTPAdapter
orig_HTTPAdapter_build_response = HTTPAdapter.build_response
def new_HTTPAdapter_build_response(self, request, resp):
    response = orig_HTTPAdapter_build_response(self, request, resp)
    try:
        response.peercert = resp.peercert
    except AttributeError:
        pass
    return response
HTTPAdapter.build_response = new_HTTPAdapter_build_response

这未经测试,因此无法保证;您可能需要修补的更多。

同样,子类化和重写可能比monkeypatching更干净(尤其是因为HTTPAdapter设计为子类化)。

或者,甚至更好的是,派生urllib3requests,修改您的派生,并(如果您认为这是合理有用的)向上游提交请求。

无论如何,现在,从您的代码中,您可以执行以下操作:
resp.peercert

这将为您提供'subject''subjectAltName'键的字典,由pyopenssl.WrappedSocket.getpeercert返回。如果您想获取有关证书的更多信息,请尝试Christophe Vandeplas's variant of this answer,它使您可以获取OpenSSL.crypto.X509对象。如果要获取整个对等证书链,请参见GoldenStake's answer

当然,您可能还希望传递验证证书所需的所有信息,但这更容易,因为它已经通过了顶层。

关于python - 如何从python中的请求获取响应SSL证书?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16903528/

10-12 04:20
查看更多