如何列出静态链接的python版本中可用的所有openssl密码?

Day*_*Day 12 python windows security openssl python-2.7

在python 2.7.8到2.7.9升级中,ssl模块从使用中更改

_DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'
Run Code Online (Sandbox Code Playgroud)

_DEFAULT_CIPHERS = (
    'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
    'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:ECDH+RC4:'
    'DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'
)
Run Code Online (Sandbox Code Playgroud)

我想知道这是如何影响在Windows上与我的python安装建立SSL/TLS连接时使用的实际"有序SSL密码首选项列表".

例如,要弄清楚密码列表扩展到什么"有序SSL密码首选项列表",我通常使用openssl ciphers命令行(参见手册页)例如用openssl v1.0.1k我可以看到默认的python 2.7.8密码列表扩展为:

$ openssl ciphers -v 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
ECDHE-RSA-AES256-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
ECDHE-ECDSA-AES256-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
SRP-DSS-AES-256-CBC-SHA SSLv3 Kx=SRP      Au=DSS  Enc=AES(256)  Mac=SHA1
SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP      Au=RSA  Enc=AES(256)  Mac=SHA1
...
snip!
Run Code Online (Sandbox Code Playgroud)

在Linux上运行时,python正在动态加载使用的相同OpenSSL库openssl ciphers:

$ ldd /usr/lib/python2.7/lib-dynload/_ssl.x86_64-linux-gnu.so | grep libssl
        libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ff75d6bf000)
$ ldd /usr/bin/openssl | grep libssl
        libssl.so.1.0.0 => /lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007fa48f0fe000)
Run Code Online (Sandbox Code Playgroud)

但是,在Windows上,Python构建似乎静态链接OpenSSL库.这意味着该openssl ciphers命令无法帮助我,因为它使用了不同版本的库,它可能支持不同于python中内置的库的密码.

我可以很容易地找到使用哪个版本的OpenSSL来构建两个python版本中的每一个:

$ python-2.7.8/python -c 'import ssl; print ssl.OPENSSL_VERSION'
OpenSSL 1.0.1h 5 Jun 2014

$ python-2.7.9/python -c 'import ssl; print ssl.OPENSSL_VERSION'
OpenSSL 1.0.1j 15 Oct 2014
Run Code Online (Sandbox Code Playgroud)

但即使我可以找到并下载openssl1.0.1h和1.0.1j版本的命令行版本,我也无法确定它们是否使用与python内置的lib相同的选项进行编译,并且从手册页我们知道

OpenSSL的某些编译版本可能不包括此处列出的所有密码,因为在编译时排除了某些密码.

那么,有没有办法让python的ssl模块给我类似于openssl ciphers -v命令的输出?

Jan*_*cke 5

您可能希望openssl cipherhttps://github.com/openssl/openssl/blob/master/apps/ciphers.c上查看源代码.

关键步骤似乎是:

  1. meth = SSLv23_server_method();
  2. ctx = SSL_CTX_new(meth);
  3. SSL_CTX_set_cipher_list(ctx, ciphers),而ciphers你的字符串
  4. ssl = SSL_new(ctx);
  5. sk = SSL_get1_supported_ciphers(ssl);
  6. for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { print SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, i)); }

SSL_CTX_set_cipher_list函数在Python 3.4中在_ssl的set_ciphers上下文方法中调用.你可以使用以下方法实现:

import socket
from ssl import SSLSocket
sslsock = SSLSocket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sslsock.context.set_ciphers('DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2')
Run Code Online (Sandbox Code Playgroud)

下一步是调用SSL_get1_supported_ciphers()哪个,遗憾的是,在Python中没有使用它_ssl.c.你可以得到的最接近的是实例的shared_ciphers()方法SSLSocket.(当前)实现是

static PyObject *PySSL_shared_ciphers(PySSLSocket *self)
{
    [...]
    ciphers = sess->ciphers;
    res = PyList_New(sk_SSL_CIPHER_num(ciphers));
    for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
        PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i));
        [...]
        PyList_SET_ITEM(res, i, tup);
    }
    return res;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,这个循环与ciphers.c上面的实现非常相似,并返回一个密码的Python列表,其顺序与循环的顺序相同ciphers.c.

继续sslsock = SSLSocket(...)上面的示例,您无法sslsock.shared_ciphers()在连接套接字之前调用.否则,Python的_ssl模块不会创建低级OpenSSL SSL对象,这是读取密码所必需的.这与实现不同ciphers.c,后者在不需要连接的情况下创建低级SSL对象.

这就是我有多远,我希望有所帮助,也许你可以根据这些发现弄清楚你需要什么.