【问题标题】:How to solve the handshake failure using ssl in python?如何在 python 中使用 ssl 解决握手失败?
【发布时间】:2019-06-22 21:26:27
【问题描述】:

我尝试连接到特定的 https 服务器:

socketHandler = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketWraped = ssl.wrap_socket(socketHandler)
socketWraped.connect(('certificatedetails.com', 443))

但是蟒蛇说:

File "/usr/lib/python3.6/ssl.py", line 1109, in connect
self._real_connect(addr, False)
File "/usr/lib/python3.6/ssl.py", line 1100, in _real_connect
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1077, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:852)

我尝试使用 TLS1:

socketWraped = ssl.wrap_socket(
    socketHandler,
    ssl_version=ssl.PROTOCOL_TLSv1,
    ciphers='ADH-AES256-SHA'
)

但是说:

ssl.SSLError: [SSL: NO_CIPHERS_AVAILABLE] no ciphers available (_ssl.c:852)

在python和操作系统中有升级的ssl:

$ hostnamectl
   Static hostname: machine
         Icon name: computer-desktop
           Chassis: desktop
        Machine ID: ...
           Boot ID: ...
  Operating System: Ubuntu 18.04.2 LTS
            Kernel: Linux 4.15.0-51-generic
      Architecture: x86-64
$ openssl version
OpenSSL 1.1.1c  28 May 2019
$ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
OpenSSL 1.1.1c  28 May 2019

从netcat可以毫无问题地连接:

$ ncat --ssl -v certificatedetails.com 443
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: SSL connection to 104.28.6.163:443.
Ncat: SHA-1 fingerprint: 75B3 C6AD 7A72 62B5 7104 0632 0585 A82A F542 641B

这是什么问题以及如何解决?

【问题讨论】:

  • ADH-AES256-SHA 似乎是一个匿名密钥交换协议;现在通常不支持它们。这就是为什么您会看到 no ciphers available - 客户端和服务器之间没有通用密码。您应该使用临时密钥交换协议,如 DH 或 ECDH。您可以在Which Cipher Suites to enable for SSL Socket? 看到它们的列表(尽管这是一个 Java 问题)。临时协议是名称中带有TLS_DHE_*TLS_ECDHE_* 的协议。
  • 仅供参考,您的“使用 TLS1”失败,因为您指定了 ADH(匿名)密码套件,并且默认构建中的 OpenSSL 1.1.0 不支持匿名套件。如果您使用默认或更合理的密码,它会以与原始代码相同的方式失败,因为 Joseph 诊断出缺少 SNI。但我不知道您为什么认为 TLS1 会有所帮助; OpenSSL 1.1.1 最高支持 TLS1.3,并且(_ssl 模块中的)python 版本可能最高支持 TLS1.2。如果您认为错误代码和文本中的 SSL3_ 和 sslv3 表示它使用的是 SSLv3 协议,那么它们不会。
  • @jww:它甚至没有走那么远。该错误意味着在本地配置 OpenSSL 的 API 调用失败(因为它不支持 ADH/匿名)。如果 OpenSSL 确实支持它,但服务器不支持(现在大多数服务器不支持),则错误可能是关于警报,或者可能只是与服务器断开连接或重置。
  • @Dave: ssl.SSLError: [SSL: NO_CIPHERS_AVAILABLE] no ciphers available (_ssl.c:852) 是运行时错误。
  • @jww:python 脚本中的所有内容都是运行时的,但我的意思是这不是因为“客户端和服务器之间不通用”;即使服务器支持匿名 - 或根本不存在,也会发生该错误。

标签: linux python-3.x openssl


【解决方案1】:

来自the documentation

从 Python 3.2 和 2.7.9 开始,建议使用 SSLContext.wrap_socket()SSLContext 实例将套接字包装为 SSLSocket 对象。辅助函数create_default_context() 返回具有安全默认设置的新上下文。老人 wrap_socket() 函数已被弃用,因为它既低效又 不支持服务器名称指示 (SNI) 和主机名匹配。

当我使用 SSLContext.wrap_socket() 而不是已弃用的 wrap_socket() 时,它可以工作:

socketHandler = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketWraped = ssl.create_default_context().wrap_socket(socketHandler, server_hostname='certificatedetails.com')
socketWraped.connect(('certificatedetails.com', 443))

【讨论】:

    【解决方案2】:

    要解决 SSLV3_ALERT_HANDSHAKE_FAILURE NO_CIPHERS_AVAILABLE 我推荐的方法是

    a) 在 linux 机器上使用 s_client 查找服务器接受的最大协议,例如:

     openssl s_client -connect my_host:443 -tls1
    

    (检查 man s_client 了解所有可能的协议)。

    b) 连接后,记下 openssl 使用的密码

    New, SSLv3, Cipher is AES256-SHA
    Server public key is 2048 bit
    Secure Renegotiation IS supported
    Compression: NONE
    Expansion: NONE
    No ALPN negotiated
    

    c) 相应地创建您的 sslcontext

    sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
    sslcontext.set_ciphers("AES256-SHA")
    sslcontext.load_default_certs()
    

    适用于所有库(请求、aiohttp...)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      • 2012-10-11
      • 2015-03-16
      • 2021-12-28
      • 2018-10-17
      • 2016-04-29
      相关资源
      最近更新 更多