https(ssl)连接之python实现
今天写代码时碰到一个问题,花了几个小时的时间google,基本上把google搜索的前几页内容都一一看了下,问题最终是解决了,不过过程挺曲折的,所以把这个过程记下来以便以后参考之。
原因是以下一段代码引起的:
1
2
|
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()) |