Сообщение об ошибке
[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failure
Также выводится как
- [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure
Код причины остаётся SSLV3_ALERT_HANDSHAKE_FAILURE, но текст зависит от OpenSSL: «ssl/tls alert handshake failure» в OpenSSL 3.3 и новее, «sslv3 alert handshake failure» в более старых версиях. Далее следует место в исходном коде, например «(_ssl.c:1082)».
Python ssl module (OpenSSL) — ssl.SSLError, TLS alert 40 (handshake_failure) from the server
Что это означает
Сервер прервал рукопожатие оповещением TLS 40 (handshake_failure). Он либо не нашёл приемлемых параметров в ClientHello от Python, либо потребовал клиентский сертификат, который не был предъявлен. Само оповещение подробностей не содержит, поэтому причину приходится искать, сравнивая, что поддерживает каждая сторона, или читая журнал сервера.
Почему это происходит
Нет общего набора шифров
частоНачиная с Python 3.10 контекст по умолчанию использует в OpenSSL уровень безопасности 2 и не предлагает ни наборов шифров без прямой секретности, ни наборов с MAC на основе SHA-1. У устаревших серверов, которые поддерживают только наборы шифров с обменом ключами RSA, с CBC-SHA или с 3DES, нет с ним ничего общего.
Сервер требует клиентский сертификат
частоЭндпоинты со взаимной TLS-аутентификацией (банковские и B2B API, внутренние сервисы) в TLS 1.2 отвечают этим оповещением на рукопожатие без клиентского сертификата.
SNI отсутствует или не настроен на сервере
иногдаПодключение по IP без server_hostname или к имени, которое CDN или прокси не обслуживает, заставляет некоторые серверы прерывать рукопожатие с оповещением handshake_failure.
Как исправить
- 1.
Сравните списки шифров
Выведите, что предлагает этот Python, и сравните со списком шифров сервера (например, полученным через nmap --script ssl-enum-ciphers). Если пересечений нет, сервер нужно обновить.
python import ssl ctx = ssl.create_default_context() print(sorted(c["name"] for c in ctx.get_ciphers())) # what this Python offers (TLS 1.2 names) - 2.
Предъявите клиентский сертификат
Для API с mTLS загрузите клиентский сертификат и ключ в контекст через load_cert_chain(); requests принимает cert=("client.pem", "client.key").
python import ssl, urllib.request ctx = ssl.create_default_context() ctx.load_cert_chain(certfile="client.pem", keyfile="client.key") # mutual TLS urllib.request.urlopen("https://mtls.example.com/", context=ctx) - 3.
Владельцам серверов: предлагайте современные наборы шифров
Связку из TLS 1.3 и TLS 1.2 с ECDHE и AES-GCM или ChaCha20-Poly1305 принимает любой клиент, который ещё получает обновления.
text # Server side: offer TLS 1.3 and TLS 1.2 ECDHE + AEAD suites ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;