如何在Python SSL中获取支持的TLS客户端版本

5
在Python SSL中,可以配置TLS客户端的密码套件和版本。使用context.set_ciphers(ciphers)设置密码套件,使用context.options设置版本。
为了确保设置正确,在握手之前甚至在设置客户端时,可以使用context.get_ciphers()获取客户端的密码套件。
我的问题是:如何获取客户端支持的协议。请注意,我没有使用默认版本。我通过使用context.options来排除某些版本。例如,这个语句从我的客户端中排除了TLS 1.1:
context.options |= ssl.OP_NO_TLSv1_1

我想确保以与密码使用context.get_ciphers()相同的方式从客户端获取TLS版本。 有没有办法做到这一点?


客户端是谁?您是服务器吗?如果是的话,连接已经建立(并且握手已完成 - 这意味着服务器和客户端找到了* TLS *协议版本(和其他内容)都支持)。context.get_ciphers()中的context是什么?因为如果它是一个ssl.SSLContext对象,它就没有一个get_ciphers()方法。 - CristiFati
@CristiFati 我是客户端。get_cipehrs() 函数显示了客户端的密码。但我正在寻找类似的函数,可以显示客户端的版本。我找不到。 - user9371654
我正在 Python3.5 上进行测试,在那里没有 get_ciphers()。你必须自己编写该功能。 - CristiFati
1个回答

4
你需要的功能在Python 3.6及更高版本中都可以找到(至少部分)。请查看[Python 3]: ssl - TLS/SSL wrapper for socket objects以获取更多详细信息:
>>> import sys
>>> import ssl
>>> "Python {:s} on {:s}".format(sys.version, sys.platform)
'Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32'
>>> ctx0 = ssl.create_default_context()
>>> ctx0.options
<Options.OP_NO_SSLv3|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION|OP_ALL: -2091252737>

对于旧版本,需要一些代码(在新版本上也适用)。

code.py:

#!/usr/bin/python3

import sys
import ssl
from pprint import pprint as pp


__PROTO_TAG = "PROTOCOL_"
__OP_NO_TAG = "OP_NO_"
__OP_NO_TAG_LEN = len(__OP_NO_TAG)
_PROTOS_DATA = list()
for item_name in dir(ssl):
    if item_name.startswith(__OP_NO_TAG) and item_name[-1].isdigit():  # item_name[-1].isdigit() condition is required because protocol denial (OP_NO_*) constants end in digit(s) (version); therefore constants like OP_NO_TICKET are excluded
        op_no_item = getattr(ssl, item_name)
        if op_no_item:
            proto_name = item_name[__OP_NO_TAG_LEN:]
            _PROTOS_DATA.append((proto_name, getattr(ssl, __PROTO_TAG + proto_name, -1), op_no_item))
del __OP_NO_TAG_LEN
del __OP_NO_TAG
del __PROTO_TAG


def get_protocols(ctx):
    supported_classes = (ssl.SSLContext,)
    if not isinstance(ctx, supported_classes):
        raise TypeError("Argument must be an instance of `{:}`".format(supported_classes[0] if len(supported_classes) == 1 else supported_classes))
    protocols = list()
    for proto_data in _PROTOS_DATA:
        if ctx.options & proto_data[-1] != proto_data[-1]:
            protocols.append(proto_data[:-1])
    return protocols


def print_data(ctx):
    print("Options: {:08X} ({!r})".format(ctx.options, ctx.options))
    print("Protocols:")
    for proto in get_protocols(ctx):
        print("    {:s} - {:d}".format(*proto))
    print()


def main():
    print("{:s}\n".format(ssl.OPENSSL_VERSION))
    ctx0 = ssl.create_default_context()
    print_data(ctx0)
    print("--- Removing TLSv1_1...")
    ctx0.options |= ssl.OP_NO_TLSv1_1
    print_data(ctx0)
    print("--- Adding SSLv3...")
    ctx0.options -= ssl.OP_NO_SSLv3  # !!! N.B.: Due to the fact that ssl.OP_NO_* flags only have one bit set, this works, but DON'T DO IT !!!
    print_data(ctx0)
    print("\nComputed protocols:")
    pp([item[:-1] + (hex(item[-1]),) for item in _PROTOS_DATA])


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

注意:

  • 由于我在这个领域工作得很广泛,所以我有很多不同的Python版本和各种操作系统上构建的不同OpenSSL版本(如下所示)
  • 尽量保持一切通用
  • 基于(ssl)模块属性编写代码;由于每个Python版本都是使用特定的OpenSSL版本构建的,因此在使用自定义组合时可能会出现意外情况(我可以硬编码OP_NO_*常量-这些常量在OpenSSL版本上是一致的,但这样做不可扩展)
  • 有ssl模块实现(特定于Python版本,依赖于特定的OpenSSL版本-如上所述),以及实际用于构建ssl模块的OpenSSL版本(可能缺少某些内容)。这就是为什么在各种组合上运行相同的代码会产生略微不同的结果(请检查下面的输出)
  • 在Win上,情况更简单,因为(默认情况下)OpenSSL被静态链接到_ssl.pyd中(从Python 3.7开始,这不再适用,OpenSSL.dll也作为Python的一部分提供),但在Nix上,OpenSSL库(安装在系统上)在运行时加载
  • 代码演示:

    • _PROTOS_DATA - 在模块导入时计算:它是基于ssl模块属性的协议条目列表。每个条目有3个字段:

      • 协议名称
      • 在ssl模块中的协议标识符(如果不存在,则为-1:例如SSLv2)
      • 用于禁用此协议的OP_NO_常量

      为了清晰起见,在每次运行结束时显示它

    • get_protocols - 确定“活动”支持的协议,适用于ssl.SSLContext
    • print_data - 帮助函数

输出

  • Win 10 x64

    e:\Work\Dev\StackOverflow\q049788677>"C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_64\python.exe" code.py
    Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32
    
    OpenSSL 1.0.2k  26 Jan 2017
    
    Options: -7CA5FC01 (<Options.OP_NO_SSLv3|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION|OP_ALL: -2091252737>)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    
    --- Removing TLSv1_1...
    Options: -6CA5FC01 (<Options.OP_NO_TLSv1_1|OP_NO_SSLv3|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION|OP_ALL: -1822817281>)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    --- Adding SSLv3...
    Options: -6EA5FC01 (<Options.OP_NO_TLSv1_1|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION|OP_ALL: -1856371713>)
    Protocols:
        SSLv3 - 1
        TLSv1 - 3
        TLSv1_2 - 5
    
    
    Computed protocols:
    [('SSLv2', -1, '0x1000000'),
     ('SSLv3', <_SSLMethod.PROTOCOL_SSLv3: 1>, '0x2000000'),
     ('TLSv1', <_SSLMethod.PROTOCOL_TLSv1: 3>, '0x4000000'),
     ('TLSv1_1', <_SSLMethod.PROTOCOL_TLSv1_1: 4>, '0x10000000'),
     ('TLSv1_2', <_SSLMethod.PROTOCOL_TLSv1_2: 5>, '0x8000000')]
    
    e:\Work\Dev\StackOverflow\q049788677>"e:\Work\Dev\VEnvs\py34x64_test\Scripts\python.exe" code.py
    Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 20:20:57) [MSC v.1600 64 bit (AMD64)] on win32
    
    OpenSSL 1.0.2d 9 Jul 2015
    
    Options: -7CFDFC01 (-2097019905)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    
    --- Removing TLSv1_1...
    Options: -6CFDFC01 (-1828584449)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    --- Adding SSLv3...
    Options: -6EFDFC01 (-1862138881)
    Protocols:
        SSLv3 - 1
        TLSv1 - 3
        TLSv1_2 - 5
    
    
    Computed protocols:
    [('SSLv2', 0, '0x1000000'),
     ('SSLv3', 1, '0x2000000'),
     ('TLSv1', 3, '0x4000000'),
     ('TLSv1_1', 4, '0x10000000'),
     ('TLSv1_2', 5, '0x8000000')]
    
    e:\Work\Dev\StackOverflow\q049788677>"c:\Install\x64\Python\Python\3.7\python.exe" code.py
    Python 3.7.0b4 (v3.7.0b4:eb96c37699, May  2 2018, 19:02:22) [MSC v.1913 64 bit (AMD64)] on win32
    
    OpenSSL 1.1.0h  27 Mar 2018
    
    Options: -7DBDFFAC (<Options.OP_NO_SSLv3|OP_CIPHER_SERVER_PREFERENCE|OP_NO_COMPRESSION|OP_ALL: -2109603756>)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    
    --- Removing TLSv1_1...
    Options: -6DBDFFAC (<Options.OP_NO_TLSv1_1|OP_NO_SSLv3|OP_CIPHER_SERVER_PREFERENCE|OP_NO_COMPRESSION|OP_ALL: -1841168300>)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    --- Adding SSLv3...
    Options: -6FBDFFAC (<Options.OP_NO_TLSv1_1|OP_CIPHER_SERVER_PREFERENCE|OP_NO_COMPRESSION|OP_ALL: -1874722732>)
    Protocols:
        SSLv3 - -1
        TLSv1 - 3
        TLSv1_2 - 5
    
    
    Computed protocols:
    [('SSLv3', -1, '0x2000000'),
     ('TLSv1', <_SSLMethod.PROTOCOL_TLSv1: 3>, '0x4000000'),
     ('TLSv1_1', <_SSLMethod.PROTOCOL_TLSv1_1: 4>, '0x10000000'),
     ('TLSv1_2', <_SSLMethod.PROTOCOL_TLSv1_2: 5>, '0x8000000')]
    
  • OSX 9 x64:

    cfati@cfati-macosx9x64-1:~/Work/Dev/StackOverflow/q049788677]> python code.py
    Python 2.7.10 (default, Oct 14 2015, 05:51:29)
    [GCC 4.8.2] on darwin
    
    OpenSSL 1.0.1p-fips 9 Jul 2015
    
    Options: 830203FF (2197947391L)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    ()
    --- Removing TLSv1_1...
    Options: 930203FF (2466382847L)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    ()
    --- Adding SSLv3...
    Options: 910203FF (2432828415L)
    Protocols:
        SSLv3 - 1
        TLSv1 - 3
        TLSv1_2 - 5
    ()
    
    Computed protocols:
    [('SSLv2', 0, '0x1000000'),
     ('SSLv3', 1, '0x2000000'),
     ('TLSv1', 3, '0x4000000'),
     ('TLSv1_1', 4, '0x10000000'),
     ('TLSv1_2', 5, '0x8000000')]
    
  • Ubtu 16 x64:

    [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049788677]> python3 code.py
    Python 3.5.2 (default, Nov 23 2017, 16:37:01)
    [GCC 5.4.0 20160609] on linux
    
    OpenSSL 1.0.2g  1 Mar 2016
    
    Options: 830203FF (2197947391)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    
    --- Removing TLSv1_1...
    Options: 930203FF (2466382847)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    --- Adding SSLv3...
    Options: 930203FF (2466382847)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    
    Computed protocols:
    [('SSLv2', -1, '0x1000000'),
     ('SSLv3', -1, '0x2000000'),
     ('TLSv1', <_SSLMethod.PROTOCOL_TLSv1: 3>, '0x4000000'),
     ('TLSv1_1', <_SSLMethod.PROTOCOL_TLSv1_1: 4>, '0x10000000'),
     ('TLSv1_2', <_SSLMethod.PROTOCOL_TLSv1_2: 5>, '0x8000000')]
    [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049788677]>
    [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049788677]> LD_LIBRARY_PATH=../q049493537/Python-3.6.4:../q049320993/ssl/build/lib ../q049493537/Python-3.6.4/python code.py
    Python 3.6.4 (default, Mar 28 2018, 23:34:25)
    [GCC 5.4.0 20160609] on linux
    
    OpenSSL 1.0.2h-fips  3 May 2016
    
    Options: 835A03FF (<Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION: 2203714559>)
    Protocols:
        TLSv1 - 3
        TLSv1_1 - 4
        TLSv1_2 - 5
    
    --- Removing TLSv1_1...
    Options: 935A03FF (<Options.OP_ALL|OP_NO_TLSv1_1|OP_NO_SSLv3|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION: 2472150015>)
    Protocols:
        TLSv1 - 3
        TLSv1_2 - 5
    
    --- Adding SSLv3...
    Options: 915A03FF (<Options.OP_ALL|OP_NO_TLSv1_1|OP_NO_SSLv2|OP_CIPHER_SERVER_PREFERENCE|OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|OP_NO_COMPRESSION: 2438595583>)
    Protocols:
        SSLv3 - -1
        TLSv1 - 3
        TLSv1_2 - 5
    
    
    Computed protocols:
    [('SSLv2', -1, '0x1000000'),
     ('SSLv3', -1, '0x2000000'),
     ('TLSv1', <_SSLMethod.PROTOCOL_TLSv1: 3>, '0x4000000'),
     ('TLSv1_1', <_SSLMethod.PROTOCOL_TLSv1_1: 4>, '0x10000000'),
     ('TLSv1_2', <_SSLMethod.PROTOCOL_TLSv1_2: 5>, '0x8000000')]
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接