代码之家  ›  专栏  ›  技术社区  ›  Dan Lenski

如何从python请求响应/异常对象访问对等方的证书链?

  •  0
  • Dan Lenski  · 技术社区  · 7 年前

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

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

    screenshot

    当我试图与 python请求 ,我得到一个不太有用的异常:

    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-cli , openssl s_client ,SSLLabs等)。

    然而,我真正想做的是能够抓住 诊断 证书链的问题 在我的Python代码中 ,以便向用户显示更具体的错误消息。 This answer 建议对响应对象使用monkey补丁;虽然它不是特别优雅,但可以工作 只有

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

    1 回复  |  直到 7 年前
        1
  •  4
  •   Sraw    7 年前

    采取 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 .