wrap_socket() 收到意外的关键字参数 'server_hostname'?
我正在尝试用Python来测试一个网络服务器。我几乎没有Python的经验,但听说它很容易学,而且做事情也很简单(这不是我自己的看法,只是别人的意见)。我正在使用的脚本是:
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.wrap_socket(s1,
ca_certs="./pki/signing-dss-cert.pem",
cert_reqs=ssl.CERT_REQUIRED,
ssl_version=ssl.PROTOCOL_TLSv1,
server_hostname="localhost")
s2.connect( ("localhost", 8443) )
s2.send("GET / ")
time.sleep(1)
s2.send("HTTP/1.1")
出现的错误是:
Traceback (most recent call last):
File "./fetch.sh", line 10, in <module>
server_hostname="localhost")
TypeError: wrap_socket() got an unexpected keyword argument 'server_hostname'
我还尝试过使用 servername
、name
、hostname
和 sni
,但是都没有成功。
Python的文档里没有提到SNI(用于套接字对象的TLS/SSL封装 和 SSL维基页面)。不过我知道,关于SNI和 server_hostname
的补丁在2010年就已经加入了(给 SSLContext.wrap_socket
添加一个 *server_hostname* 参数, 更改集 65593:846c0e1342d0)。
我需要访问的等效OpenSSL调用是 SSL_set_tlsext_host_name
。
我该如何指定SNI主机名呢?(最终,我需要把它设置为一个任意的名称,因为我在测试一个代理)。
2 个回答
我的环境是:requests==2.7.0,python-2.7.5-34.el7.x86_64,gevent==1.0.2
把Python版本换成python-2.7.5-18.el7_1.1.x86_64后,问题就解决了。
在CentOS上:
sudo rpm -Uvh --oldpackage python-devel-2.7.5-18.el7_1.1.x86_64.rpm python-libs-2.7.5-18.el7_1.1.x86_64.rpm python-2.7.5-18.el7_1.1.x86_64.rpm
可以在谷歌上搜索相关的包。
你提到的这个补丁是针对Python 3.2的,而你现在用的是Python 2.7。问题5639似乎也表明没有计划将SNI支持移植到Python 2.7上。
你可以考虑用pyOpenSSL来包装socket,它的Connection
类从0.13版本开始就有一个set_tlsext_host_name
方法。(我不太确定Debian 7.3自带哪个版本,你可以考虑设置一个虚拟环境,然后根据需要升级到更新的版本。)
在pyOpenSSL的库里有一个SNI示例。
如果你希望你的wrap_socket
用法能更好地兼容你在httplib
连接中替换sock
的做法,可以看看urllib3是如何用pyOpenSSL实现的。基本上,它是从一个现有的socket创建一个OpenSSL.SSL.Connection
,但由于这个连接和socket不兼容,它会把它包装成一个实现所需方法的类。
(顺便提一下,在Python 2.7中,urllib
、urllib2
和httpconnection
根本不进行任何证书验证,除非你自己通过包装它们的socket来实现。)
编辑:
这里有一个可以在Python 3.2中工作的代码版本。不幸的是,server_name
参数不在普通的ssl.wrap_socket
中,只有在SSLContext.wrap_socket
里,但你可以直接使用SSLSocket
。
import socket
import ssl
CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt"
HOST = "sni.velox.ch"
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.SSLSocket(sock=s1, ca_certs=CA_BUNDLE_FILE,
cert_reqs=ssl.CERT_REQUIRED,
ssl_version=ssl.PROTOCOL_TLSv1,
server_hostname=HOST)
s2.connect((HOST, 443))
s2.send(bytes("GET / HTTP/1.1\n", "UTF-8"))
# This might need to be modified when using another port
s2.send(bytes("Host: %s\n" % (HOST,), "UTF-8"))
s2.send(bytes("\n", "UTF-8"))
# Certainly not the best way to read the response, but it works.
while True:
x = s2.read()
if not x:
break
print(x)