Python3.4中使用asyncio和aiohttp出现SSLV3握手失败问题

4

我遇到了以下代码的问题,出现了SSLV3握手失败的错误:

import aiohttp
import asyncio
import ssl

def main():
    conn = set_conn()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(get_thing('https://example.com', conn))

@asyncio.coroutine
def get_thing(url, conn):
    response = yield from aiohttp.request('get', url, connector=conn)
    print(response.text)

def set_conn():
    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    context.verify_mode = ssl.CERT_REQUIRED
    context.check_hostname = True
    context.load_verify_locations('/path/to/cert.pem')
    conn = aiohttp.TCPConnector(ssl_context=context)
    return conn

if __name__ == "__main__":
    main()

堆栈跟踪:

Traceback (most recent call last):
  File "/Users/grahat03/workspace/ent/env/lib/python3.4/site-packages/aiohttp/connector.py", line 344, in _create_connection
    **kwargs))
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 437, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 453, in _create_connection_transport
    yield from waiter
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/futures.py", line 348, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/tasks.py", line 370, in _wakeup
    value = future.result()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/futures.py", line 243, in result
    raise self._exception
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/selector_events.py", line 605, in _on_handshake
    self._sock.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/ssl.py", line 805, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:598)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/grahat03/workspace/ent/env/lib/python3.4/site-packages/aiohttp/connector.py", line 164, in connect
    transport, proto = yield from self._create_connection(req)
  File "/Users/grahat03/workspace/ent/env/lib/python3.4/site-packages/aiohttp/connector.py", line 348, in _create_connection
    (req.host, req.port)) from exc
aiohttp.errors.ClientOSError: Can not connect to example.com:443

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "stackoverflow.py", line 26, in <module>
    main()
  File "stackoverflow.py", line 10, in main
    loop.run_until_complete(get_thing(urls[0], conn))
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 208, in run_until_complete
    return future.result()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/futures.py", line 243, in result
    raise self._exception
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/tasks.py", line 317, in _step
    result = coro.throw(exc)
  File "stackoverflow.py", line 14, in get_thing
    response = yield from aiohttp.request('get', url, connector=conn)
  File "/Users/grahat03/workspace/ent/env/lib/python3.4/site-packages/aiohttp/client.py", line 104, in request
    conn = yield from connector.connect(req)
  File "/Users/grahat03/workspace/ent/env/lib/python3.4/site-packages/aiohttp/connector.py", line 168, in connect
    raise ClientOSError() from exc
aiohttp.errors.ClientOSError

我使用的是Mac OSX 10.9.5操作系统,Python版本为:
python3 -c "import sys; print(sys.version)"
3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 00:54:21)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]

OpenSSL似乎没问题,我可以按照以下方式进行连接:

openssl s_client -connect example.com:443 -cert /path/to/cert.pem

我怀疑在创建SSL上下文时有些地方没有做对,你有什么想法吗?


我可以使用 requests 轻易地检索资源。 - tfgrahame
顺便问一下,你确定是 SSLV3 吗?在你的例子中,你使用了不同的协议——SSLV23 - Andrew Svetlov
我也曾经想过这个问题,但堆栈跟踪对应的是我使用的代码。 - tfgrahame
嗯,不知道。你可以在https://github.com/KeepSafe/aiohttp/issues上提交一个错误报告,但我怀疑我能否帮助你。我需要一个代码示例来重现你的情况,但是使用“example.com”和没有真实证书是不可能的。自签名对和手工制作的aiohttp.web服务器可能可以。 - Andrew Svetlov
你尝试过使用仅限于 context.wrap_socket() 而不是 asyncio 和/或 aiohttp 来复现问题吗? - jfs
显示剩余2条评论
2个回答

0
答案是使用不同的SSLContext方法:
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain('/path/to/cert.pem')

这与aiohttp无关。


0
我注意到即使在requests和curl没有问题的情况下,aiohttp也可能出现这个错误。你可以使用curl来找到一个可用的SSL上下文。
> curl -v <url> > /dev/null
...
* (304) (OUT), TLS handshake, Client hello (1):
} [318 bytes data]
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
...
* SSL connection using TLSv1.2 / AES256-GCM-SHA384

实验一下看看到底需要什么。在我的情况下,只需要密码。
import ssl
import aiohttp

# use one of:
#ssl_context = ssl.create_default_context()
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="/etc/ssl/cert.pem")

# optional
ssl_context.options = ssl.PROTOCOL_TLSv1_2
ssl_context.set_ciphers("AES256-GCM-SHA384")

# create session or pass to each request with `ssl=ssl_context`
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ssl_context)) as s:
    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接