使用urllib2进行SSLv3警报握手失败

kar*_*kpl 5 python ssl openssl urllib2 python-2.7

我在使用Python 2.7.10下的urllib2连接https时遇到了麻烦.

我想念的是什么?

Python 2.7.10 (default, Jun 18 2015, 10:53:24) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl, urllib2
>>> ssl.HAS_SNI
True
>>> ssl.OPENSSL_VERSION
'OpenSSL 0.9.8o 01 Jun 2010'
>>> opener = urllib2.build_opener()
>>> opener.open('https://twitrss.me/')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/python2.7/lib/python2.7/urllib2.py", line 431, in open
    response = self._open(req, data)
  File "/usr/local/python2.7/lib/python2.7/urllib2.py", line 449, in _open
    '_open', req)
  File "/usr/local/python2.7/lib/python2.7/urllib2.py", line 409, in _call_chain
    result = func(*args)
  File "/usr/local/python2.7/lib/python2.7/urllib2.py", line 1240, in https_open
    context=self._context)
  File "/usr/local/python2.7/lib/python2.7/urllib2.py", line 1197, in do_open
    raise URLError(err)
urllib2.URLError: <urlopen error [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:590)>
Run Code Online (Sandbox Code Playgroud)

rha*_*oto 2

我能够在 OS X 10.10.3 上重复您的问题,其库存 Python 是使用 OpenSSL 0.9.8zd 构建的 2.7.6。

问题是 TLS 握手中缺少服务器名称指示(SNI) 扩展,而 twitrss.me 站点显然需要该扩展:

服务器名称指示 (SNI) 是 TLS 计算机网络协议的扩展,客户端通过该协议在握手过程开始时指示它尝试连接到的主机名。

我通过使用 OpenSSL 编写一个小型 C++ 程序并插入 OpenSSL 调用来验证这一点

SSL_set_tlsext_host_name(ssl, "twitrss.me");
Run Code Online (Sandbox Code Playgroud)

允许连接成功,同时忽略失败。我还查看了数据包转储,以验证在尝试使用 Python 连接时是否缺少 SNI。

Python SSL 模块显然支持 Python 3 中的 SNI,但可能需要 Python 2 中的解决方法。看来PEP 0466包含 SNI 并登陆了 Python 2.7.9,所以你应该拥有它,但我不知道是否可以urllib2/urllib3在没有解决方法的情况下利用它。