首页 > 解决方案 > 如何从 python-requests 响应/异常对象访问对等方的证书链?

问题描述

我使用python-requests与 HTTPS Web 服务通信,其中一些提供不完整的证书 X509 链。我无法弄清楚如何访问无效/不完整的证书以便向用户解释错误。

这是https://ssllabs.com/ssltest说明的示例,其中服务器仅发送叶证书,而不是验证所需的中间证书,但从certifi的根 CA 存储中丢失:

截屏

当我尝试连接python-requests时,我得到一个不是很有用的异常:

request.get('https://web.service.com/path')

SSLError: HTTPSConnectionPool(host='web.service.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

显然,我可以使用单独的工具来找出任何特定情况下的问题(例如gnutls-cliopenssl s_clientSSLLabs 等)。

但是,我真正想做的是能够在我的 Python 代码中捕捉和诊断证书链的问题,以便向用户呈现更具体的错误消息。这个答案暗示了响应对象的猴子补丁;它不是特别优雅,但它可以工作——尽管只有在成功返回响应对象时,而不是在异常的情况下。

requests当请求无法验证证书链本身时,将对等方的证书链保存在返回的异常对象中的最简洁方法是什么 ?

标签: pythonexception-handlingpython-requestsssl-certificatex509

解决方案


requests.get("https://151.101.1.69") # stackoverflow's ip个例子:

try:
    requests.get("https://151.101.1.69")
except requests.exceptions.SSLError as e:
    cert = e.args[0].reason.args[0]._peer_cert

然后cert是一个 dict 包含对等方的证书。由于我不是很熟悉SSL,我不知道这是否足以满足您的情况。

顺便说一句,在这种情况下,错误是"hostname '151.101.1.69' doesn't match either of '*.stackexchange.com', ...omitted. 我不确定您真实案例中的异常结构,因此您可能需要自己找到它。我认为它应该具有相同的名称_peer_cert

更新

握手失败时上述方法不起作用......但它仍然可以做到:

try:
    requests.get("https://fpslinux1.finalphasesystems.com/")
except requests.exceptions.SSLError:
    import ssl
    import OpenSSL
    cert = ssl.get_server_certificate(('fpslinux1.finalphasesystems.com', 443))
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
    print(cert.get_issuer())
    print(cert.get_subject().get_components())

是的,它有点脏,但我没有更好的方法,因为 ssl 套接字甚至不会从 C 级别返回无效证书:/

要使用OpenSSL,您需要安装pyopenssl


推荐阅读