wrap_socket()得到了一个意外的关键字参数'server_hostname'?

时间:2014-03-13 18:26:07

标签: python ssl sni

我尝试使用Python来测试Web服务器。我几乎没有使用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'

我也尝试过使用servernamenamehostnamesni而不感到高兴。

Python文档不提及SNI(TLS/SSL wrapper for socket objectsSSL wiki page)。但是我知道SNI和server_hostname的补丁是在4年前的2010年(Add a *server_hostname* argument to SSLContext.wrap_socketchangeset 65593:846c0e1342d0)合并而来的。

我需要访问的等效OpenSSL调用是SSL_set_tlsext_host_name

如何指定SNI主机名? (最后,我需要将其设置为任意名称,因为我正在测试代理)。

3 个答案:

答案 0 :(得分:2)

patch you're mentioning适用于Python 3.2,您使用的是Python 2.7。 Issue 5639似乎也表明没有计划为Python 2.7支持SNI支持。

您可以使用pyOpenSSL包装套接字(其Connection类自版本0.13开始有set_tlsext_host_name。(我不确定Debian 7.3附带哪个版本,您可能需要设置一个virtualenv并在必要时在本地升级到更新版本。)

SNI example是pyOpenSSL存储库。

如果您希望wrap_socket的使用与该技巧更加兼容,那么您可以在sock连接中替换httplib的值,那么您可以查看一下{{ 3}}。本质上,它从现有套接字创建OpenSSL.SSL.Connection,但由于该连接与套接字不兼容,因此它将其包装到实现所需方法的类中。

(顺便说一下,在Python 2.7中,urlliburllib2httpconnection根本不进行任何证书验证,除非您自己通过包装套接字来实现它。)

编辑:

以下是适用于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)

答案 1 :(得分:1)

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

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客户端通常比使用原始套接字要容易得多。

答案 2 :(得分: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上:

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string str;
    cout << "CONVERSION\n\n";
    cout << "Base 11 to Decimal\n";
    cout << "Base 11: ";
    getline(std::cin, str);
    const auto bad_loc = str.find_first_not_of("0123456789aA");
    if (bad_loc != std::string::npos) 
    {
        throw "bad input"; // or whatever handling
    }
    unsigned long ul = std::stoul(str, nullptr, 11);
    cout << "Decimal: " << ul << '\n';
    return 0;
}

pakages可以在谷歌搜索。