Python sll
import
urllib2
urllib2.urlopen(
'https://xxxx.com'
)
本来这段代码很简单的,就是请求一个https的连接,可是报以下错误:
urllib2.URLError:
第一反应是https证书问题产生的,如是以'python ssl' 为关键字google后,看到大家都在用'requests'这个python组件做http请求客户端,就像java里面的httpclient组件一样,如果安装完request包后,改成如下代码:
import
requests
requests.get(
'https://xxx.com'
)
还是报以下错误:
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客户端运行如下测试脚本:
wget https:
//xxx
.com
果然报如下错误:
OpenSSL: error:140773E8:SSL routines:SSL23_GET_SERVER_HELLO:reason(1000)
无法建立 SSL 连接。
到这里我怀疑是openssl安装有问题,更新到最新版本后还是一样,然后在浏览器里访问是可以的,所以应该不是openssl有问题。继续google.......,就发现有人也遇到过这种问题,说是连接SSL服务器时SSL的版本不对,如是用如下代码测试不同的SSL版本,看是不是这个问题:
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代码了。、
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实现:
# 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的版本号。