尝试从 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.raw
是urllib3.response.HTTPResponse
),但是在许多情况下这是不可能的。这就是其中一种情况。唯一可以看到证书的对象是
http.client.HTTPSConnection
(或urllib3.connectionpool.VerifiedHTTPSConnection
,但这只是前者的子类)和ssl.SSLSocket
,当请求返回时,这些对象都不存在。 (正如connectionpool
的名称所暗示的,HTTPSConnection
对象存储在池中,并且可以在完成后立即重用; SSLSocket
是HTTPSConnection
的成员。)因此,您需要打补丁,以便可以将数据复制到整个链中。可能就这么简单:
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
设计为子类化)。或者,甚至更好的是,派生
urllib3
和requests
,修改您的派生,并(如果您认为这是合理有用的)向上游提交请求。无论如何,现在,从您的代码中,您可以执行以下操作:
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/