Programming languages

Java SSLHandshakeException: handshake_failure explained

By · Last verified

Error messagejavax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

SSLHandshakeException: Received fatal alert: handshake_failure means the server ended the TLS handshake because it could not find settings both sides accept. It is not a certificate-trust error: the usual causes are no shared protocol or cipher suite, a missing SNI hostname, or a server that demands a client certificate the Java client did not present.

Quick fix

The handshake trace shows which parameter failed, so get it first:

java -Djavax.net.debug=ssl:handshake -jar app.jar 2>&1 | tee tls-debug.log

Then check what the server accepts from a neutral machine:

openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null
  • Neither works: the server only offers old protocols (TLS 1.0/1.1) or ciphers the JDK disables. Upgrade the server's TLS configuration; the TLS config generator gives a modern baseline.
  • openssl works but Java fails: compare the ClientHello in the Java log with the server's suites, check SNI, and check whether the server asks for a client certificate (Acceptable client certificate CA names in the openssl output).

Causes

1. No common protocol version or cipher suite

Current JDKs disable SSLv3, TLS 1.0, TLS 1.1 and weak algorithms such as RC4 and 3DES through jdk.tls.disabledAlgorithms in $JAVA_HOME/conf/security/java.security. A server limited to those fails immediately.

  • Confirm: the SSL checker shows only TLS 1.0/1.1, or openssl s_client -tls1_2 fails.
  • Fix: enable TLS 1.2 and TLS 1.3 with ECDHE + AES-GCM or ChaCha20-Poly1305 suites on the server. Look up any suite name with the cipher suite lookup.

2. Client explicitly restricted

The reverse also happens: an application sets https.protocols=TLSv1, jdk.tls.client.protocols, or a fixed setEnabledCipherSuites list from years ago, and the modern server rejects it.

  • Confirm: the ClientHello in the debug log offers only old versions or a short suite list.
  • Fix: remove the hard-coded protocol and cipher lists and let the JDK defaults apply.

3. SNI missing

Servers that host many names on one IP (CDNs, load balancers, Nginx with several server blocks) may abort when no server_name extension arrives.

  • Confirm: the debug log's ClientHello has no server_name extension; or you connect by IP.
  • Fix: connect by hostname, remove -Djsse.enableSNIExtension=false, and make sure custom SSLSocketFactory code passes the host name.

4. The server requires a client certificate

With mutual TLS, a server can reject a client that sends no certificate or one from an untrusted CA.

  • Confirm: openssl s_client lists acceptable client CA names; the Java log shows a CertificateRequest followed by an empty Certificate message.
  • Fix: configure a keystore with the client certificate and key:
java -Djavax.net.ssl.keyStore=client.p12 -Djavax.net.ssl.keyStoreType=PKCS12 \
     -Djavax.net.ssl.keyStorePassword=secret -jar app.jar

If the client certificate came from a public CA, check clientAuth EKU removal: public CAs stopped issuing certificates usable for client authentication in 2026.

5. Key type or signature algorithm mismatch

An ECDSA-only server and a client that disabled EC curves, or a server certificate signed with SHA-1, can also end in this alert. The debug log names the rejected algorithm.

Fixes by environment

Find the side that restricts the handshake, then align protocols, ciphers, SNI or the client certificate there.

Old Java 8 builds: old Java 8 updates lack TLS 1.3 and may restrict strong AES keys. Update to a current Java 8 update or move to a supported LTS release.

Spring Boot / Apache HttpClient / OkHttp: these libraries use the JVM's JSSE settings unless you pass custom protocols or ciphers. Search the configuration for TLSv1, SSLv3 or explicit cipher lists.

Behind a load balancer: the handshake may be terminated by the balancer, not the application server. Check the balancer's TLS policy rather than Tomcat's.

Containers: slim JRE images use the same java.security defaults; a handshake that fails in the container and works on your laptop usually means a different Java version, so print java -version in both.

Don't do this

  • Removing TLS 1.0 and 1.1 from jdk.tls.disabledAlgorithms globally. Every connection from that JVM becomes downgradable. If unavoidable, isolate the legacy call on a separate JVM and set a removal date.
  • Copying a cipher list from an old blog post. Hard-coded lists age badly and cause the next handshake failure.
  • Installing a trust-all TrustManager. It does nothing for handshake_failure and removes certificate checks.

FAQ

Is handshake_failure a certificate trust problem?

Usually not. Trust problems produce PKIX path building failed on the client side. Received fatal alert: handshake_failure means the server sent alert 40 and gave up, typically before the client even saw the certificate. Look at protocols, ciphers, SNI and client certificates first.

How do I see which side caused it?

Run the Java client with -Djavax.net.debug=ssl:handshake and look at the last messages before the alert. If the ClientHello lists only TLS 1.2/1.3 suites and the server answers with the alert, the server cannot use any of them. Compare with openssl s_client against the same host and with an SSL checker report of the server's supported protocols.

Can I re-enable TLS 1.0 in Java to reach an old server?

You can edit jdk.tls.disabledAlgorithms in the java.security file, but it weakens every connection that JVM makes. Treat it as a short, isolated workaround on a dedicated JVM while the server is upgraded to TLS 1.2 or 1.3.

Why does it fail only for one hostname on a shared IP?

That points to SNI. If the client connects by IP address, disables SNI with -Djsse.enableSNIExtension=false, or a proxy strips it, the server cannot pick the right virtual host and may abort the handshake.

Sources

  1. Java Secure Socket Extension (JSSE) Reference Guide
  2. openssl-s_client (OpenSSL 3.0)
  3. keytool (Java SE 21 tool reference)