Сообщение об ошибке
javax.net.ssl.SSLHandshakeException: (handshake_failure) Received fatal alert: handshake_failure
Также выводится как
- javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
- Received fatal alert: handshake_failure
JDK 21 и более поздние версии показывают название оповещения в скобках: «(handshake_failure) Received fatal alert: handshake_failure»; JDK 8 и 11 выводят только «Received fatal alert: handshake_failure».
Java (JSSE) — TLS alert 40 received from the server
Что это означает
Сервер прервал TLS-рукопожатие оповещением 40 (handshake_failure). Либо в ClientHello от JVM он не нашёл ничего приемлемого (стороны не сошлись ни в наборе шифров, ни в группе, ни в алгоритме подписи), либо он требует клиентский сертификат, который JVM не предъявила. Java сообщает только о том, что пришло оповещение; причину можно найти в журнале сервера или увидеть с помощью javax.net.debug.
Почему это происходит
Сервер предлагает только наборы шифров, отключённые в JDK
частоАктуальные обновления JDK (в наших тестах — 21.0.12 и 25.0.4) перечисляют в jdk.tls.disabledAlgorithms наборы TLS_RSA_* (обмен ключами RSA), 3DES, RC4 и анонимные наборы. Устаревшие серверы, на которых разрешены только эти наборы, не работают ни с одной обновлённой JVM.
Взаимная TLS-аутентификация без клиентского сертификата
частоСервер запросил сертификат, а у JVM его не было (не задан javax.net.ssl.keyStore или в нём нет сертификата, выпущенного CA, которому доверяет сервер).
SNI не передан или неизвестен серверу
иногдаЕсли подключаться по IP или с jsse.enableSNIExtension=false, CDN и мультитенантные серверы прерывают рукопожатие.
Очень старая JVM и современный сервер
иногдаВ Java 7 и ранних обновлениях Java 8 нет наборов шифров ECDHE/GCM и включённого по умолчанию TLS 1.2, которых требуют серверы с усиленной защитой.
Как исправить
- 1.
Посмотрите, как проходит рукопожатие
javax.net.debug=ssl:handshake выводит ClientHello (предложенные наборы шифров) и оповещение; -XshowSettings:security:tls показывает включённые в JDK протоколы и наборы шифров.
bash # Show what the JVM offers and how the handshake ends java -Djavax.net.debug=ssl:handshake -jar app.jar 2>&1 | grep -E 'ClientHello|cipher_suites|Alert|fatal' # Settings in effect (protocols, suites) for this JDK java -XshowSettings:security:tls -version - 2.
Владельцам серверов: включите современные наборы шифров
TLS 1.3 и TLS 1.2 с ECDHE и AES-GCM или ChaCha20 работают со всеми поддерживаемыми JDK.
text # Server side: offer ECDHE + AEAD suites that current JDKs support 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; - 3.
Взаимная TLS-аутентификация: настройте хранилище ключей клиента
Клиентский сертификат берётся из javax.net.ssl.keyStore (или из SSLContext, созданного на основе KeyManagerFactory).
bash # Mutual TLS: give the JVM the client certificate and key java -Djavax.net.ssl.keyStore=client.p12 -Djavax.net.ssl.keyStorePassword=changeit \ -Djavax.net.ssl.keyStoreType=PKCS12 -jar app.jar - 4.
Крайняя мера: измените disabledAlgorithms для одного устаревшего клиента
Файл переопределения, переданный через -Djava.security.properties, может убрать TLS_RSA_* из jdk.tls.disabledAlgorithms для одной JVM; скопируйте полное значение по умолчанию и удалите только то, что нужно. У этих наборов нет прямой секретности, поэтому исключение должно быть узким и временным.
bash # Last resort for one legacy server: take this JDK's own value and drop only TLS_RSA_* v=$(sed -n '/^jdk.tls.disabledAlgorithms=/,/[^\\]$/p' "$JAVA_HOME/conf/security/java.security" | sed 's/\\$//' | tr -d '\n') printf '%s\n' "$v" | sed -E 's/, TLS_RSA_\*//' > legacy-rsa.security java -Djava.security.properties=legacy-rsa.security -jar legacy-client.jar
Связанные ошибки
No appropriate protocol (protocol is disabled or cipher suites are inappropriat…Java (JSSE)javax.net.ssl.SSLHandshakeException: (protocol_version) Received fatal alert: p…Java (JSSE)SSL routines:ssl3_read_bytes:ssl/tls alert handshake failureOpenSSL (client side)SSL routines:tls_post_process_client_hello:no shared cipherOpenSSL (server side: nginx, Apache, Postfix, openssl s_server)