【问题标题】:aiohttp and client-side SSL certificatesaiohttp 和客户端 SSL 证书
【发布时间】:2017-06-01 18:59:02
【问题描述】:

我最近从 flask + requests 转移到 aiohttp 及其异步 http 客户端。

在我的场景中,我需要通过 HTTPS(使用自定义证书)调用 API 并发送客户端证书。

对于第一部分(验证自定义证书),支持很明确 clearly documented int the docs 并且效果很好。

另一方面,对于第二部分,我似乎无法找到一种简单的方法来附加自定义 SSL 客户端证书以授权客户端。

你们知道怎么做吗?非常感谢!

【问题讨论】:

    标签: python ssl certificate client-certificates aiohttp


    【解决方案1】:

    编辑:我已经提交了PR,其中包含有关该主题的 aiohttp 文档的更新,它已被合并。

    对于将来可能遇到此问题的任何人..

    TL:DR

    import ssl
    import aiohttp    
    
    ssl_ctx = ssl.create_default_context(cafile='/path_to_client_root_ca')
    ssl_ctx.load_cert_chain('/path_to_client_public_key.pem', '/path_to_client_private_key.pem')
    
    conn = aiohttp.TCPConnector(ssl_context=ssl_ctx)
    session = aiohttp.ClientSession(connector=conn)
    
    # session will now send client certificates..
    

    长话短说 - 我已经查看了它是如何在请求中实现的(它整齐地记录了 API here),显然它是在 urllib3 内部实现的。

    urllib3 将cert 参数一直向下传递到其HTTPSConnection 对象,并最终在该对象处调用此函数:

    ...
    self.sock = ssl_wrap_socket(
        sock=conn,
        keyfile=self.key_file,
        certfile=self.cert_file,
        ssl_context=self.ssl_context,
    )
    ...
    

    这样做:

    ...
    if ca_certs or ca_cert_dir:
        try:
            context.load_verify_locations(ca_certs, ca_cert_dir)
        except IOError as e:  # Platform-specific: Python 2.6, 2.7, 3.2
            raise SSLError(e)
        # Py33 raises FileNotFoundError which subclasses OSError
        # These are not equivalent unless we check the errno attribute
        except OSError as e:  # Platform-specific: Python 3.3 and beyond
            if e.errno == errno.ENOENT:
                raise SSLError(e)
            raise
    elif getattr(context, 'load_default_certs', None) is not None:
        # try to load OS default certs; works well on Windows (require Python3.4+)
        context.load_default_certs()
    
    if certfile:
        context.load_cert_chain(certfile, keyfile)
    if HAS_SNI:  # Platform-specific: OpenSSL with enabled SNI
        return context.wrap_socket(sock, server_hostname=server_hostname)
    ...
    

    这里有趣的调用是load_cert_chain——这意味着如果我们只创建一个ssl.SSLContext(这是一个标准库接口)对象并使用我们的客户端证书调用load_cert_chain,aiohttp 将表现相同作为请求\urllib3。

    因此,尽管 aiohttp 的文档没有告诉您这一点,但他们确实指定您可以加载自己的 ssl.SSLContext

    【讨论】:

    • 注意,除非我添加了一个目的:ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH),否则当服务器尝试对客户端进行身份验证时,我无法使其正常工作。将其留在这里以供将来参考!
    • 我们在验证类似于this issue 的远程服务器证书时遇到问题。所以我们最终像这样加载了服务器的根证书:ssl_ctx = ssl.create_default_context(cafile='certs/entrust_ev_ca.cer') 在这种情况下,可以添加客户端证书/密钥吗? ssl_ctx.load_cert_chain('client.crt', 'client.key') 我问是因为在示例中我假设对 create_default_context 的调用是指客户端的根 CA,而不是服务器的...
    猜你喜欢
    • 2010-10-16
    • 2012-07-19
    • 1970-01-01
    • 2013-06-20
    • 2013-08-04
    • 2017-04-28
    • 2012-02-15
    • 2012-10-18
    • 2013-10-01
    相关资源
    最近更新 更多