# Java No appropriate protocol: SSLHandshakeException fixed

> Java throws No appropriate protocol when every enabled TLS version is disabled or lacks a usable cipher suite, usually code pinned to TLSv1.

Source: https://ssl.sh/errors/java-no-appropriate-protocol/
Last updated: 2026-11-10

**`javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)` means Java has no TLS version left that it is allowed to use.** Each enabled protocol is disabled by the JDK security policy or lacks a usable cipher suite. The error occurs before Java contacts the server; code that pins TLS 1.0 or 1.1 is the usual cause.

## What the error means

Java found no TLS version that is both enabled and usable, so it never sent a ClientHello. When a handshake starts, JSSE builds its list of "active protocols": the enabled protocols (from `SSLContext`, `setEnabledProtocols`, `jdk.tls.client.protocols` or a library setting) that are not blocked by `jdk.tls.disabledAlgorithms` and that have at least one enabled cipher suite that works with them. If that list is empty, `HandshakeContext` throws this exact exception. No ClientHello is sent.

Since JDK 8u291 (April 20, 2021), 11.0.11 and 16, `jdk.tls.disabledAlgorithms` contains `TLSv1` and `TLSv1.1`. Any code that asks only for those versions now ends up with an empty list. Newer JDKs disable more: Oracle's crypto roadmap notes that TLS_RSA cipher suites are disabled in JDK 24 and later, so code that pins only such suites fails the same way.

Compare this with a related message. If your client enables TLS 1.2/1.3 but the server can only do TLS 1.0, the handshake gets further and fails with `The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]`. Different message, different side to fix.

## Causes, most common first

Code or configuration that pins TLS 1.0 or 1.1 is the most common cause.

### 1. Code or a library pins TLSv1 or TLSv1.1

Typical forms: `SSLContext.getInstance("TLSv1")`, `socket.setEnabledProtocols(new String[] {"TLSv1"})`, HTTP client builders with a protocol array, `-Dhttps.protocols=TLSv1`, mail properties such as `mail.smtp.ssl.protocols=TLSv1`, or a JDBC URL parameter that limits TLS versions in an older driver.

- **Confirm:** search code and configuration:

```bash
grep -rnE "TLSv1([^.]|\.1)|SSLv3|https\.protocols|ssl\.protocols|enabledTLSProtocols|tlsVersions" src/ config/ *.properties *.yml 2>/dev/null
```

Then run once with `-Djavax.net.debug=ssl:handshake` and look for `No available cipher suite for ...` or ignored protocols.

- **Fix:** remove the pin and use `SSLContext.getInstance("TLS")`, which lets the JDK enable the newest versions it supports (TLS 1.3 and 1.2 on current releases). Upgrade old drivers and libraries that hardcode TLS 1.0.

### 2. A Java server is configured with old protocols only

Tomcat, Jetty or Spring Boot configured with something like `server.ssl.enabled-protocols=TLSv1` fails on the server side for every client after a JDK update.

- **Fix:** `server.ssl.enabled-protocols=TLSv1.3,TLSv1.2` in Spring Boot, or list only `TLSv1.2` and `TLSv1.3` in the `protocols` attribute of Tomcat's `SSLHostConfig`.

### 3. Pinned cipher suites that are disabled or do not fit

"Cipher suites are inappropriate" covers code that sets enabled cipher suites to a list where nothing is usable: only `TLS_RSA_*` suites on JDK 24+, only 3DES or RC4 suites, or only TLS 1.3 suites while TLS 1.3 is not enabled. Look them up in the [cipher suite lookup](https://ssl.sh/tools/cipher-suite-lookup/).

- **Fix:** stop pinning cipher suites in application code, or pin modern ECDHE AEAD suites.

### 4. A hardened or custom java.security

A company image, FIPS configuration or `-Djava.security.properties` file may disable more than the default.

- **Confirm:**

```bash
grep -n -A4 "^jdk.tls.disabledAlgorithms" "$JAVA_HOME/conf/security/java.security"          # JDK 9+
grep -n -A4 "^jdk.tls.disabledAlgorithms" "$JAVA_HOME/jre/lib/security/java.security"      # JDK 8
ps -ef | grep -o "java.security.properties=[^ ]*"
```

## Fix commands by platform

Remove the TLS 1.0 or 1.1 pin and let the JDK negotiate TLS 1.2 or 1.3.

**Any OS, JVM options for a client:**

```bash
java -Djdk.tls.client.protocols=TLSv1.3,TLSv1.2 -Dhttps.protocols=TLSv1.3,TLSv1.2 -jar app.jar
```

**Code:**

```java
SSLContext ctx = SSLContext.getInstance("TLS");   // not "TLSv1"
ctx.init(null, null, null);                         // default trust store, verification on
```

**Spring Boot (application.properties):**

```properties
server.ssl.enabled-protocols=TLSv1.3,TLSv1.2
```

**Windows services:** JVM options usually live in the service wrapper configuration (for Tomcat, the Java tab of `tomcat9w.exe` or the `JAVA_OPTS` of the service); add the same `-D` flags there.

**The remote system supports only TLS 1.0:** the correct fix is upgrading that system. As a temporary, scoped exception, give only that JVM an override file:

```properties
# /opt/app/legacy-tls.security
# Paste the exact jdk.tls.disabledAlgorithms value from your JDK's java.security,
# then delete only "TLSv1," and "TLSv1.1," from it. Keep every other entry.
jdk.tls.disabledAlgorithms=SSLv3, DTLSv1.0, RC4, DES, <...rest of your JDK's list...>
```

```bash
java -Djava.security.properties=/opt/app/legacy-tls.security -jar app.jar
```

With a single `=`, the file appends to the master `java.security` and wins for the keys it defines, for this process only.

## Don't disable certificate verification

Installing a trust-all `TrustManager`, setting a `HostnameVerifier` that returns `true`, or importing random certificates into `cacerts` does nothing for this error, because the handshake never starts. Those changes only make the application accept forged certificates, exposing credentials and data to anyone on the network path.

Also avoid editing the JDK's global `java.security` to remove `TLSv1`: every Java application on the machine then accepts deprecated protocols. The safer alternatives are, in order: remove protocol pins, upgrade the peer to TLS 1.2+, and only if unavoidable, a per-JVM override file with a removal date.

## Verify the fix

```java
// TlsCheck.java — run with: java TlsCheck.java example.com   (JDK 11+)
import javax.net.ssl.*;

public class TlsCheck {
    public static void main(String[] args) throws Exception {
        SSLSocketFactory f = SSLContext.getDefault().getSocketFactory();
        try (SSLSocket s = (SSLSocket) f.createSocket(args[0], 443)) {
            SSLParameters p = s.getSSLParameters();
            p.setEndpointIdentificationAlgorithm("HTTPS");   // keep hostname verification on
            s.setSSLParameters(p);
            s.startHandshake();
            System.out.println(s.getSession().getProtocol() + " " + s.getSession().getCipherSuite());
        }
    }
}
```

Run it with the same JDK and JVM options as your application. It should print `TLSv1.3` or `TLSv1.2` and a cipher suite. `openssl s_client -connect example.com:443 -servername example.com -tls1_2` confirms from outside Java that the server accepts TLS 1.2. The [SSL checker](https://ssl.sh/tools/ssl-checker/) shows which versions a public server supports; the [TLS config generator](https://ssl.sh/tools/tls-config-generator/) gives a modern server configuration if you own the other side. If the handshake now gets further but fails on certificates, continue with [PKIX path building failed](https://ssl.sh/errors/java-pkix-path-building-failed/).
