<!-- https://pkizerotohero.com/errors/java-handshake-failure/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Fix Java's Received fatal alert: handshake_failure: see why the server gave up with javax.net.debug, then fix cipher suites or the client certificate.

Source: https://pkizerotohero.com/errors/java-handshake-failure/ · Last reviewed: 2026-10-20

- Java
- Exact text checked with the real tool

# Received fatal alert: handshake\_failure

Also seen in: Spring, Apache HttpClient, OkHttp on the JVM, Maven, Gradle, Tomcat, Kafka clients

**In one sentence:**

The server refused the TLS handshake Java offered: usually no shared cipher suite, or the server wants a client certificate that Java did not send.

[Check yours: Paste your error](https://pkizerotohero.com/tools/error-finder/)

## Where you see it

In any Java program that opens a TLS connection: `HttpsURLConnection`, `java.net.http.HttpClient`, Spring’s `RestTemplate` and `WebClient`, Apache HttpClient, JDBC drivers, Kafka clients, and build tools such as Maven and Gradle.

```text
javax.net.ssl.SSLHandshakeException: (handshake_failure) Received fatal alert: handshake_failure
```

Java 23 and later, and recent updates of Java 17 and 21, put the alert name in brackets in front, as above. Older versions, including current Java 8 and 11, print only `Received fatal alert: handshake_failure`. The text comes from OpenJDK’s [Alert.java](https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/sun/security/ssl/Alert.java). Frameworks wrap it, so search the stack trace for `SSLHandshakeException`.

## What it means

“Received” is the key word: the **server** sent a fatal `handshake_failure` alert and closed the [TLS handshake](https://pkizerotohero.com/handbook/glossary/#tls-handshake). Java is not complaining about the server’s certificate; that would be [PKIX path building failed](https://pkizerotohero.com/errors/java-pkix-path-building-failed/). In RFC 9846, the alert means the sender “was unable to negotiate an acceptable set of security parameters given the options available” ([section 6.2](https://www.rfc-editor.org/rfc/rfc9846#section-6.2)). In practice the server found no [cipher suite](https://pkizerotohero.com/handbook/glossary/#cipher-suite) it shares with Java, or it wanted a [client certificate](https://pkizerotohero.com/handbook/glossary/#client-certificate) and got none.

To see where it stops, run the program once with Java’s TLS debug output:

```bash
java -Djavax.net.debug=ssl:handshake -jar app.jar
```

```text
javax.net.ssl|DEBUG|10|main|2026-10-20 04:12:57.967 CEST|null:-1|Produced ClientHello handshake message (
javax.net.ssl|DEBUG|10|main|2026-10-20 04:12:57.976 CEST|null:-1|Received alert message (
  "level"      : "fatal",
  "description": "handshake_failure"
```

An alert straight after the ClientHello points to versions or cipher suites. An alert after a `CertificateRequest` points to the client certificate.

## Likely causes

### No cipher suite in common

The server accepts only cipher suites that this Java version does not offer, or the other way round. Current Java updates disable many old suites in `jdk.tls.disabledAlgorithms`, including `TLS_RSA_*` (we found it in Java 11, 17, 21 and 25 builds from 2026; Java 8 still allowed it). A server that offers only those suites then fails with exactly this error.

**Confirm** which suites the server accepts, and look at Java’s list:

```bash
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 </dev/null 2>/dev/null | grep 'Cipher is'
grep -A6 '^jdk.tls.disabledAlgorithms' "$JAVA_HOME/conf/security/java.security"
```

On Java 8 the file is `$JAVA_HOME/jre/lib/security/java.security` (or `lib/security` in a JRE).

**Fix it on the server:** enable TLS 1.2 with ECDHE and AES-GCM or ChaCha20-Poly1305 suites, and TLS 1.3. See [How to configure TLS securely](https://pkizerotohero.com/guides/tls-configuration/). If the server is very old and cannot be changed, put a modern reverse proxy in front of it.

### The server wants a client certificate

With [mutual TLS](https://pkizerotohero.com/handbook/glossary/#mutual-tls), the server asks for a client certificate. Java sends one only if its keystore holds a key whose certificate was issued by one of the CAs the server lists as acceptable. If none matches, Java sends an empty certificate message, and the server fails the handshake, typically with `handshake_failure` in TLS 1.2 and `certificate_required` in TLS 1.3. With TLS 1.2 the debug output shows the line below; with TLS 1.3 it shows `No available client authentication scheme`.

```text
No X.509 certificate for client authentication, use empty Certificate message instead
```

**Confirm** which CAs the server accepts:

```bash
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | grep -A3 'Acceptable client certificate CA names'
```

**Fix:** point Java at a keystore with the right key and certificate chain, issued by one of those CAs:

```bash
java -Djavax.net.ssl.keyStore=client.p12 -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.keyStorePassword=changeit -jar app.jar
```

Include the intermediate in the PKCS #12 file, and check that the certificate has not expired. See [How to set up mutual TLS](https://pkizerotohero.com/guides/mutual-tls-setup/).

### The server only speaks an old TLS version

Then the alert is usually `protocol_version` rather than `handshake_failure`, but some servers send `handshake_failure` for this too. **Confirm** with `openssl s_client -tls1_2` and `-tls1_3`; **fix** by enabling TLS 1.2 and 1.3 on the server.

Warning

Do not weaken Java to make it connect

Removing entries from `jdk.tls.disabledAlgorithms`, or enabling TLS 1.0 again, changes the rules for every connection the JVM makes, not just this one. Those algorithms were disabled because they are weak. Fix the server’s configuration, or put a modern proxy in front of a server you cannot change.

## Check yours

*Interactive tool: Paste your error (runs in the browser at [/tools/error-finder/](https://pkizerotohero.com/tools/error-finder/))*

Paste the exception or the last debug lines. The finder shows the matching page, so you can tell a handshake problem from a certificate problem.

## Learn why

- [The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/): how the client and server agree on versions and cipher suites.
- [Both sides show ID: mutual TLS](https://pkizerotohero.com/handbook/mutual-tls/): how a server asks for client certificates.
- Related: [ERR\_SSL\_VERSION\_OR\_CIPHER\_MISMATCH](https://pkizerotohero.com/errors/chrome-err-ssl-version-or-cipher-mismatch/) and [tlsv1 alert unknown ca](https://pkizerotohero.com/errors/openssl-tlsv1-alert-unknown-ca/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/java-handshake-failure/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 15: The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/)
- [Chapter 23: Both sides show ID: mutual TLS](https://pkizerotohero.com/handbook/mutual-tls/)

## Fix it step by step

- [How to install a certificate in Tomcat and other Java servers](https://pkizerotohero.com/guides/install-certificate-tomcat/)
- [How to configure TLS securely (versions, ciphers, HSTS)](https://pkizerotohero.com/guides/tls-configuration/)

## Everything on this topic

- [TLS and HTTPS](https://pkizerotohero.com/topics/tls/)
