Urllib.urlopen() работает с URL-адресами SSLv3 с Python 2.6.6 на 1 машине, но не с 2.6.7/2.7.2 на другой

Провел большую часть дня на этом, и я верю в свой остроумный конец. У меня есть 1 машина "A" с установленным Python 2.6.6/2.7.2, и установлена ​​другая машина "B" с установленным Python 2.6.7/2.7.2.

На машине A я могу получить SSLv3-зашифрованный веб-сайт с urllib2.urlopen('https://fed.princeton.edu'), используя Python 2.6.6, но не 2.7.2.

На машине B я не могу получить этот сайт, используя либо версию Python.

Не могу получить, я имею в виду, что я получаю ошибку:

Traceback:
File "/usr/local/lib/python2.7/dist-packages/Django-1.3.1-py2.7.egg/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django_cas-2.0.3-py2.7.egg/django_cas/views.py" in login
  78.         user = auth.authenticate(ticket=ticket, service=service)
File "/usr/local/lib/python2.7/dist-packages/Django-1.3.1-py2.7.egg/django/contrib/auth/__init__.py" in authenticate
  55.             user = backend.authenticate(**credentials)
File "/usr/local/lib/python2.7/dist-packages/django_cas-2.0.3-py2.7.egg/django_cas/backends.py" in authenticate
  72.         username = _verify(ticket, service)
File "/usr/local/lib/python2.7/dist-packages/django_cas-2.0.3-py2.7.egg/django_cas/backends.py" in _verify_cas2
  46.     page = urlopen(url)
File "/usr/lib/python2.7/urllib.py" in urlopen
  84.         return opener.open(url)
File "/usr/lib/python2.7/urllib.py" in open
  205.                 return getattr(self, name)(url)
File "/usr/lib/python2.7/urllib.py" in open_https
  435.             h.endheaders(data)
File "/usr/lib/python2.7/httplib.py" in endheaders
  954.         self._send_output(message_body)
File "/usr/lib/python2.7/httplib.py" in _send_output
  814.         self.send(msg)
File "/usr/lib/python2.7/httplib.py" in send
  776.                 self.connect()
File "/usr/lib/python2.7/httplib.py" in connect
  1161.             self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
File "/usr/lib/python2.7/ssl.py" in wrap_socket
  372.                      ciphers=ciphers)
File "/usr/lib/python2.7/ssl.py" in __init__
  134.                 self.do_handshake()
File "/usr/lib/python2.7/ssl.py" in do_handshake
  296.         self._sslobj.do_handshake()

Exception Type: IOError at /login
Exception Value: [Errno socket error] [Errno 1] _ssl.c:503: error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert unexpected message

Во-первых, я смущен тем, что что-то, что работает на более ранней версии Python, не работает на более позднем этапе Machine A. Я также очень смущен тем, что что-то, что работает на 2.6.6, не работает на 2.6. 7 (хотя и на разных машинах). Почему это должно быть?

Теперь я не уверен, что конфигурация для Python точно такая же для обоих, но import _ssl и import httplib; httplib.HTTPSConnection работают для всех версий на обеих машинах. Я также пробовал curl -v https://fed.princeton.edu и openssl fed.princeton.edu:https на обеих машинах, и все эти команды работают.

Я также провел некоторое исследование и нашел Как использовать urllib2 для получения веб-страницы с использованием шифрования SSLv3, где автор, похоже, отказался от urllib для libCurl ( Я бы предпочел не с тех пор, как я использую django-cas, в котором используется urllib, и я не хочу слишком много разбираться в этом коде).


Примечание: я только что нашел http://bugs.python.org/issue11220, и последнее решение для почты позволяет мне использовать urlopen для открыть сайт. Но как я могу использовать их решение (которое, как представляется, использовать urllib2.install_opener(urllib2.build_opener(HTTPSHandlerV3()))?) Для разрешения моего urlopen() в django-cas?

Ответ 1

После немного большего количества экспериментов я только что согласился с тем, что Python 2.6.6 в порядке, но у версии 2.6.7+ есть ошибка в том, что вы не можете получить зашифрованные SSLv3 страницы через urllib.urlopen().

Я решил свою проблему, просто используя трюк urllib2.install_opener в http://bugs.python.org/issue11220 и modded django_cas, чтобы этот открыватель был установлен до urlopen() вызов.

Ответ 2

Вы можете обезглавить патч ssl.wrap_socket(), переопределив параметр ключевого слова ssl_version. Следующий код может использоваться как есть. Поместите это перед urlopen().

import ssl
from functools import wraps
def sslwrap(func):
    @wraps(func)
    def bar(*args, **kw):
        kw['ssl_version'] = ssl.PROTOCOL_TLSv1
        return func(*args, **kw)
    return bar

ssl.wrap_socket = sslwrap(ssl.wrap_socket)

EDIT: я обновил код выше, поняв, что functools.partial фактически не возвращает функцию и не подходит в этом случае. Коротко, как он мог бы выглядеть, приведенный выше код по-прежнему является лучшим решением, которое я знаю до сих пор.