3

I'm trying to use Python to test a web server. I have nearly no experience with Python, but encouraged to use it because its easy to learn and simple to do things with (someone else's opinion, not mine at the moment). The script I am using is:

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")

The error is:

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'

I've also tried using servername, name, hostname and sni with no joy.

The Python docs don't mention SNI (TLS/SSL wrapper for socket objects and SSL wiki page). But I know the patch for SNI and server_hostname was incorporated 4 years ago in 2010 (Add a *server_hostname* argument to SSLContext.wrap_socket, changeset 65593:846c0e1342d0).

The equivalent OpenSSL call I need access to is SSL_set_tlsext_host_name.

How do I specify the SNI hostname? (Eventually, I'll need to set it to an arbitrary name because I am testing a proxy).

4

3 回答 3

2

首先连接,然后包装套接字。

import socket, ssl
sock = socket.create_connection( ('localhost', 443) )
sock = ssl.wrap_socket(sock, ca_certs="./pki/signing-dss-cert.pem", cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1)

在 python2 的情况下,我有时也会使用以下 hack(因为httplib.HTTPSConnection文档说它不对 https 服务器证书执行任何类型的检查):

import urllib, httplib, ssl

PATH = urllib.quote(u'/'.encode('utf-8'))
conn = httplib.HTTPConnection('www.google.com', 443)
conn.connect()
try:
    conn.sock = ssl.wrap_socket(conn.sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CA_BUNDLE_FILE, ssl_version=VERSION)
    conn.request('GET', PATH)
    resp = conn.getresponse()
    print resp.status
    print resp.read()
finally:
    conn.close()

请注意,如果您想与服务器通信,那么使用 http 客户端通常比使用原始套接字更容易。

于 2014-03-13T18:33:11.367 回答
2

您提到的补丁适用于 Python 3.2,而您使用的是 Python 2.7。问题 5639似乎也表明没有计划向后移植 Python 2.7 的 SNI 支持。

您可以改用 pyOpenSSL 包装套接字(它的Connectionset_tlsext_host_name从 0.13 版本开始。(我不确定 Debian 7.3 附带哪个版本,如果需要,您可能希望设置一个 virtualenv 并在本地升级到更新版本。)

有一个SNI 示例是 pyOpenSSL 存储库。

如果您希望您的使用与替换连接中wrap_socket的值的技巧更兼容,您可以查看urllib3 如何使用 pyOpenSSL 执行此操作。本质上,它从现有套接字创建一个,但由于该连接与套接字不兼容,因此它将其包装到实现所需方法的类中。sockhttplibOpenSSL.SSL.Connection

(顺便说一句,在 Python 2.7 中,urllib根本urllib2不做httpconnection任何证书验证,除非你自己通过包装它们的套接字来实现它。)

编辑:

这是您的代码版本,应该适用于 Python 3.2。不幸的是,该server_name参数不在 plainssl.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)
于 2014-03-13T23:20:22.440 回答
0

我的环境是: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

包可以在google上搜索。

于 2016-03-02T07:50:45.680 回答