SSL客户端证书不是由python的'requests'库或者s_client发送的,但是在web浏览器中工作正常

我正在尝试使用Python编写客户端脚本,访问Web应用程序并使用SSL客户端证书进行身份validation。 尽pipe我可以从Firefox和Chrome访问应用程序,只要我已经加载了客户端证书,每当我通过Python请求发送请求时,我会得到以下响应:

400 No required SSL certificate was sent nginx/1.4.6 (Ubuntu) 

我也尝试过Python httplib,s_client和curl,并得到相同的错误信息。 我使用相同的客户端证书进行所有testing,使用pkcs12格式的Web浏览器,并将其导出到命令行工具的证书和关键PEM文件中。 我的Python代码如下所示:

 import requests CERT = r'/path/to/cert.crt' #Client certificate KEY = r'/path/to/key.key' #Client private key CACERT = r'/path/to/ca.crt' #Server certificate chain session = requests.Session() session.cert = (CERT, KEY) resp = session.get('https://my.webapp.com/', verify=CACERT) print resp.content session.close() 

s_client给出了关于正在发生的事情的更多信息。 以下是输出的缩写版本:

 $ openssl s_client -cert cert.crt -key key.key -CAfile ca.crt -connect <host>:<port> CONNECTED(00000003) depth=2 /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA verify return:1 depth=1 /C=US/O=GeoTrust, Inc./CN=RapidSSL CA verify return:1 depth=0 /serialNumber=tgBIwyM-p18O/aDyvyWNKHDnOezzDJag/OU=GT89519184/OU=See www.rapidssl.com/resources/cps (c)13/OU=Domain Control Validated - RapidSSL(R)/CN=*.rexdb.us verify return:1 --- Certificate chain ... --- Server certificate -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- subject=... --- No client certificate CA names sent --- SSL handshake has read 3519 bytes and written 328 bytes --- New, TLSv1/SSLv3, Cipher is DHE-RSA-AES128-SHA Server public key is 4096 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : DHE-RSA-AES128-SHA Session-ID: ... Session-ID-ctx: Master-Key: ... Key-Arg : None Start Time: 1409269239 Timeout : 300 (sec) Verify return code: 0 (ok) --- GET / HTTP/1.1 Host: my.webapp.com HTTP/1.1 400 Bad Request Server: nginx/1.4.6 (Ubuntu) Date: Thu, 28 Aug 2014 23:41:04 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: close ... <head><title>400 No required SSL certificate was sent</title></head> ... closed 

我相当肯定,这不是服务器的问题,因为身份validation在浏览器中工作。 但是,s_client输出显示“没有客户端证书CA名称发送”,这听起来像是一个服务器问题(例如,客户端证书没有被发送到服务器,因为服务器没有要求)。 这里是nginxconfiguration的相关部分:

 ssl on; ssl_certificate_key /path/to/server/key.pem; ssl_certificate /path/to/server/certificate.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK; ssl_prefer_server_ciphers on; ssl_session_timeout 5m; ssl_client_certificate /path/to/client/ca/certificate.crt; ssl_verify_depth 10; ssl_verify_client on; 

该应用程序通过nginx使用uwsgi。 虚拟机上有多个虚拟主机,只有这个虚拟主机使用证书authentication。

我唯一可以find的其他潜在的相关区别是,在Firefox中,连接是保持活动状态,而在s_client中它是非常接近的。 我已经试过在头文件中设置Connection:keep-alive,结果相同。

虚拟机上有多个虚拟主机,只有这个虚拟主机使用证书认证。

我假设这意味着你有相同的IP地址背后的多个证书,并且客户端必须使用SNI(服务器名称指示)在SSL握手中向服务器发送期望的主机名。 openssl s_client在默认情况下不使用SNI,我不知道python是否执行 – 它可能取决于您使用的python版本。

因为客户端只发送一个证书,如果服务器告诉它它可能会这样做,那是因为缺少SNI,你会遇到错误的配置部分,这是另一个证书的默认部分,不需要客户端证书。

我建议再次尝试使用openssl s_client ,但是使用命令行选项-servername (在手册页中没有记录,但是如果使用-h调用则显示)来显式设置期望的服务器名称。 如果这个工作,你需要找到一种方法来使用Python的SNI。 如果这不起作用,请进行数据包转储,并确保与wireshark,服务器确实需要客户端发送证书,客户端确实不会发送证书。