requests
故意包装这样的低级内容。通常,您唯一想做的就是验证证书是否有效。为此,只需通过verify=True
。如果您想使用非标准 cacert 包,您也可以通过它。例如:
resp = requests.get('https://example.com', verify=True, cert=['/path/to/my/ca.crt'])
Also, requests
主要是一组其他库的包装器,主要是urllib3和标准库的http.client(或者,对于 2.x,httplib
) and ssl.
有时,答案只是获取较低级别的对象(例如,resp.raw
is the urllib3.response.HTTPResponse
),但在很多情况下这是不可能的。
这就是其中之一。唯一看到证书的对象是http.client.HTTPSConnection
(or a 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
这是未经测试的,所以不能保证;您可能需要修补更多内容。
此外,子类化和重写可能比猴子补丁更干净(特别是因为HTTPAdapter
被设计为子类化)。
或者,更好的是,分叉urllib3
and requests
,修改您的分叉,以及(如果您认为这确实有用)向上游提交拉取请求。
无论如何,现在,从您的代码中,您可以执行以下操作:
resp.peercert
这会给你一个字典'subject'
and 'subjectAltName'
键,由返回pyopenssl.WrappedSocket.getpeercert
。如果您想了解有关证书的更多信息,请尝试Christophe Vandeplas 这个答案的变体这可以让你得到一个OpenSSL.crypto.X509
目的。如果您想获取整个对等证书链,请参阅金币的回答.
当然,您可能还想传递验证证书所需的所有信息,但这更容易,因为它已经通过了顶层。