【问题标题】:Python Paste SSL server with TLSv1.2 and Forward Secrecy使用 TLSv1.2 和前向保密的 Python 粘贴 SSL 服务器
【发布时间】:2015-11-12 16:09:07
【问题描述】:

对于 Mac OS X 上的 Python 应用程序,我需要设置一个具有 TLSv1.2、前向保密且没有 RC4 密码的 HTTPS 服务器。使用 Paste 和 pyOpenSSL 我编写了以下代码:

from paste import httpserver
from OpenSSL import SSL

context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file("/Path/to/my/private.key")
context.use_certificate_chain_file("/Path/to/my/chain-cert.pem")
context.set_options(SSL.OP_NO_SSLv2)
context.set_options(SSL.OP_NO_SSLv3)
context.set_options(SSL.OP_SINGLE_DH_USE)
context.set_cipher_list("EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4")
logger.debug("OPENSSL version: %s" % SSL.SSLeay_version(SSL.SSLEAY_VERSION))

httpserver.serve(app_logged, host=http_host, port=http_port, server_version=server_version, ssl_context=context, use_threadpool=True, threadpool_workers=15, request_queue_size=5)

但 Safari、Chrome 和 OpenSSL 客户端无法连接到我的服务器,并出现错误“no shared cipher”。那么,我做错了什么?

注意:默认 OS X 版本的 Python (2.7.6) 和 OpenSSL (0.9.8) 与 TLSv1.2 不兼容,因此我必须编译 OpenSSL 1.0.2 和 Python 2.7 .10 来源。

如果我检查我的 OpenSSL 密码列表,它会给我这个:

$ /usr/local/bin/openssl ciphers -V 'EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4'
0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128) Mac=AEAD
0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA256
0xC0,0x28 - ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA256
0xC0,0x14 - ECDHE-RSA-AES256-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
0xC0,0x0A - ECDHE-ECDSA-AES256-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
0xC0,0x13 - ECDHE-RSA-AES128-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA1
0xC0,0x09 - ECDHE-ECDSA-AES128-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA1
0x00,0x9F - DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
0x00,0x6B - DHE-RSA-AES256-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA256
0x00,0x39 - DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
0x00,0x88 - DHE-RSA-CAMELLIA256-SHA SSLv3 Kx=DH       Au=RSA  Enc=Camellia(256) Mac=SHA1
0x00,0x9E - DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(128) Mac=AEAD
0x00,0x67 - DHE-RSA-AES128-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA256
0x00,0x33 - DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
0x00,0x9A - DHE-RSA-SEED-SHA        SSLv3 Kx=DH       Au=RSA  Enc=SEED(128) Mac=SHA1
0x00,0x45 - DHE-RSA-CAMELLIA128-SHA SSLv3 Kx=DH       Au=RSA  Enc=Camellia(128) Mac=SHA1

而且我知道 Safari 8 与密码“ECDHE-RSA-AES128-SHA256”兼容,那么为什么会出现“无共享密码”错误?

$ /usr/local/bin/openssl s_client -connect 192.168.0.17:4443 -tls1_2
CONNECTED(00000003)
140735274361680:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:s3_pkt.c:1472:SSL alert number 40
140735274361680:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol  : TLSv1.2
Cipher    : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg   : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1439975452
Timeout   : 7200 (sec)
Verify return code: 0 (ok)
---

替代方案:如果我将密码列表更改为

context.set_cipher_list("HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:@STRENGTH")

它可以工作,浏览器和 openssl 客户端能够使用协议 TLSv1.2 上的密码“AES256-GCM-SHA384”建立安全连接,但它不是 ECDHE 密码(因此没有前向保密)。

编辑正确答案:

对于 DH 密码,您需要一个 PEM 格式的 DH 参数文件,您可以使用以下命令生成一个:

$ /usr/local/bin/openssl dhparam 2048 -out dhparams.pem

对于 ECDHE 密码,您需要为 SSL 上下文设置椭圆曲线。您可以使用 pyOpenSSL 检索系统上的可用曲线:

OpenSSL.crypto.get_elliptic_curves()

这给了我们正确的以下python代码:

from paste import httpserver
from OpenSSL import SSL
from OpenSSL import crypto

context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file("/Path/to/my/private.key")
context.use_certificate_chain_file("/Path/to/my/chain-cert.pem")
context.load_tmp_dh("/Path/to/my/dhparams.pem")
context.set_tmp_ecdh(crypto.get_elliptic_curve("prime256v1"))
context.set_options(SSL.OP_NO_SSLv2)
context.set_options(SSL.OP_NO_SSLv3)
context.set_options(SSL.OP_SINGLE_DH_USE)
context.set_cipher_list("EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4")
logger.debug("OPENSSL version: %s" % SSL.SSLeay_version(SSL.SSLEAY_VERSION))

httpserver.serve(app_logged, host=http_host, port=http_port, server_version=server_version, ssl_context=context, use_threadpool=True, threadpool_workers=15, request_queue_size=5)

【问题讨论】:

  • 您可以尝试使用openssl s_server -cipher ... 代替 paste/python 来确定它是否与 openssl 安装或 paste/python 有关?
  • 好主意@SteffenUllrich,$ /usr/local/bin/openssl s_server -cipher 'EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4' -accept 4443 -cert /Path/to/my/chain-cert.pem -key /Path/to/my/private.key -no_ssl2 -no_ssl3 -www 似乎有效。 $ /usr/local/bin/openssl s_client -connect 192.168.0.17:4443 -tls1_2 连接是使用 TLSv1.2 协议和 ECDHE-RSA-AES256-GCM-SHA384 密码建立的。似乎是 python/paste 或 pyOpenSSL 的问题?
  • 我成功使用了这个的最终版本,并且还获得了 ssl labs 的 A 评级。我不得不升级 pyOpenSSL。见:stackoverflow.com/questions/34045618/…

标签: python macos ssl openssl


【解决方案1】:

我将把它留在这里,因为它也适用于内置库。

import ssl
from socket import *

context = ssl.create_default_context()
context.set_ciphers('ECDHE+AESGCM:!ECDSA')
context.load_dh_params("dhparams.pem")
context.set_ecdh_curve("prime256v1")

sock = socket()
sock.connect(('hostname', 443))
sock = context.wrap_socket(sock, server_side=False, server_hostname='hostname')

由于上述答案和问题是关于 pyOpenSSL,我知道这可能不是 100% 在正确的球场。

但是经过一番搜索为什么我得到ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1045) 却没有任何细节说明为什么会发生这种情况,这篇文章挽救了我的一天。但我也认为可以通过ssl 本地完成(我'确定,可能使用 OpenSSL,但仍然认为这在这里可能有用)。

【讨论】:

    【解决方案2】:

    要支持 DH 密码 (DHE-RSA-...),您需要有一个 DH 参数文件并指定它:

     context.load_tmp_dh("dhparams.pem")
    

    要创建这样的文件,您可以使用 OpenSSL

     openssl dhparam -out dhparams.pem 2048
    

    要支持 ECDH 密码,您需要指定应使用的曲线。我没有支持它的 pyOpenSSL 版本,但根据 documentation 它应该是

    context.set_tmp_ecdh( OpenSSL.crypto.get_elliptic_curve( "prime256v1" ))
    

    【讨论】:

    • 太棒了!它可以工作,只是有点错字,它是get_elliptic_curve('prime256v1')get_elliptic_curves() 方法用于列出您当前版本的 OpenSSL 支持的曲线。我现在拥有 Safari 使用的密码 ECDHE_RSA_WITH_AES_256_CBC_SHA384,并且在 ssllabs.com SSL 服务器测试中获得了 'A' 评级。非常感谢!
    • @BuvinJ:cmets 不适用于新问题。如果您想得到答案,请提出一个新问题并添加必要的详细信息以重现该问题。
    • @BuvinJ:只需提出一个新问题,不要忘记添加重现问题所需的所有信息。
    猜你喜欢
    • 1970-01-01
    • 2020-01-11
    • 2022-11-18
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多