Trying to get the SSL certificate from a response in
What is a good way to do this?
Thanks very much!
requests deliberately wraps up low-level stuff like this. Normally, the only thing you want to do is to verify that the certs are valid. To do that, just pass
verify=True. If you want to use a non-standard cacert bundle, you can pass that too. For example:
resp = requests.get('https://example.com', verify=True, cert=['/path/to/my/ca.crt'])
Sometimes, the answer is just to get at the lower-level objects (e.g.,
resp.raw is the
urllib3.response.HTTPResponse), but in many cases that's impossible.
And this is one of those cases. The only objects that ever see the certs are an
http.client.HTTPSConnection (or a
urllib3.connectionpool.VerifiedHTTPSConnection, but that's just a subclass of the former) and an
ssl.SSLSocket, and neither of those exist anymore by the time the request returns. (As the name
connectionpool implies, the
HTTPSConnection object is stored in a pool, and may be reused as soon as it's done; the
SSLSocket is a member of the
So, you need to patch things so you can copy the data up the chain. It may be as simple as this:
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
That's untested, so no guarantees; you may need to patch more than that.
Also, subclassing and overriding would probably be cleaner than monkeypatching (especially since
HTTPAdapter was designed to be subclassed).
Or, even better, forking
requests, modifying your fork, and (if you think this is legitimately useful) submitting pull requests upstream.
Anyway, now, from your code, you can do this:
Of course you may also want to pass along all the information necessary to verify the cert, but that's even easier, because it already passes through the top level.