https(ssl)连接之python实现

  • A+
所属分类:Python

import urllib2

urllib2.urlopen('https://xxxx.com')

本来这段代码很简单的,就是请求一个https的连接,可是报以下错误:

?

1

urllib2.URLError:

第一反应是https证书问题产生的,如是以'python ssl' 为关键字google后,看到大家都在用'requests'这个python组件做http请求客户端,就像java里面的httpclient组件一样,如果安装完request包后,改成如下代码:

?

1

2

import requests

requests.get('https://xxx.com')

 还是报以下错误:

?

1

requests.exceptions.SSLError: [Errno 1] _ssl.c:504:
error:140773E8:SSL
routines:SSL23_GET_SERVER_HELLO:reason(1000)

可以看出来,用requests和urllib2报的错误信息是一样,可见它们都是基于相同的底层api操作的,比如基于TLS的socket连接。到这里的时候我怀疑这个问题不是python代码写的有问题,可能是操作系统级别的设置错了。如下直接在shell客户端运行如下测试脚本:

?

1

wget https://xxx.com

果然报如下错误:

?

1

2

OpenSSL: error:140773E8:SSL
routines:SSL23_GET_SERVER_HELLO:reason(1000)

无法建立 SSL 连接。

到这里我怀疑是openssl安装有问题,更新到最新版本后还是一样,然后在浏览器里访问是可以的,所以应该不是openssl有问题。继续google.......,就发现有人也遇到过这种问题,说是连接SSL服务器时SSL的版本不对,如是用如下代码测试不同的SSL版本,看是不是这个问题:

?

1

2

3

4

5

curl -1 https://xxx.com

 

curl -2 https://xxx.com

 

curl -3 https://xxx.com

分别用上面的三句脚本去测试连接情况,发现第三种可以连接正常(-1,2,3,数字分别代码tlsv1,sslv2,sslv3三个不同的SSL版本)。说明这个https连接所在的服务器是基于SSLV3版本的。找到的问题,就很容易知道怎么改写python代码了。

?

1

2

3

4

5

6

7

8

9

class MyAdapter(HTTPAdapter):

    def init_poolmanager(self,
connections, maxsize):

        self.poolmanager= PoolManager(num_pools=connections,

            maxsize=maxsize,

            ssl_version=ssl.PROTOCOL_SSLv3)

 

s= requests.Session()

s.mount('https://',
MyAdapter())
#所有的https连接都用ssl.PROTOCOL_SSLV3去连接

s.get('https://xxx.com')

urllib2实现:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# custom HTTPS opener, banner's oracle 10g server supports SSLv3
only

import httplib,
ssl, urllib2, socket

class HTTPSConnectionV3(httplib.HTTPSConnection):

    def __init__(self,*args,**kwargs):

        httplib.HTTPSConnection.__init__(self,*args,**kwargs)

         

    def connect(self):

        sock= socket.create_connection((self.host,self.port),self.timeout)

        if self._tunnel_host:

            self.sock= sock

            self._tunnel()

        try:

            self.sock= ssl.wrap_socket(sock,self.key_file,self.cert_file,
ssl_version
=ssl.PROTOCOL_SSLv3)

        except ssl.SSLError,
e:

            print("Trying
SSLv3."
)

            self.sock= ssl.wrap_socket(sock,self.key_file,self.cert_file,
ssl_version
=ssl.PROTOCOL_SSLv23)

             

class HTTPSHandlerV3(urllib2.HTTPSHandler):

    def https_open(self,
req):

        return self.do_open(HTTPSConnectionV3,
req)

# install opener

urllib2.install_opener(urllib2.build_opener(HTTPSHandlerV3()))

 

if __name__== "__main__":

    r= urllib2.urlopen("https://ui2web1.apps.uillinois.edu/BANPROD1/bwskfcls.P_GetCrse")

    print(r.read())

可以看到这两种方案的原理都是一样,就是自定义连接处理器,改变连接时ssl的版本号。

参考文章:http://bugs.python.org/issue11220

              https://github.com/kennethreitz/requests/issues/606

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: