If you encounter javax.net.ssl.SSLHandshakeException while developing or deploying Java applications on WildFly, Spring Boot, Quarkus, or microservices, it often indicates a mismatch between your client and server TLS configurations. This exception can result from expired certificates, missing trusted CA certificates, or unsupported TLS versions (TLS 1.0/1.1 being deprecated) in your JVM or server configuration. In this updated guide, you will learn practical, step-by-step methods to diagnose and solve SSLHandshakeException in Java, ensuring your applications remain secure and compliant with modern TLS 1.2 and TLS 1.3 standards while maintaining connectivity to HTTPS endpoints.
Connecting Securely to a Server
To connect securely to a server, you first need to get the server’s public certificate. Save the certificate in a file and add it to your computer’s list of trusted certificates. This list of trusted certificates is the trust store and you can find it located in a file cacerts. You can find it in a folder named security in the Java installation directory.
To add the certificate to the trust store, you need to run a program called keytool with the certificate file, a meaningful name, and the path to the cacerts file.
keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>
Once you have completed these steps, you can communicate securely with the server provided that both JVMs (client/server) are using the following properties with the correct values:
java -Djavax.net.ssl.keyStore=path_to_keystore_file
-Djavax.net.ssl.keyStorePassword=password
-Djavax.net.ssl.trustStore=path_to_truststore_file
-Djavax.net.ssl.trustStorePassword=password MyClass
If you have followed the above steps and you are facing javax.net.ssl.SSLHandshakeException then you need to check for some possible causes.
Causes of the Issue
Here are some possible solutions to fix the issue javax.net.ssl.SSLHandshakeException:
- You need to update your SSL/TLS protocol version: Make sure that you’re using the latest SSL/TLS protocol version that your web server supports. Some older versions of SSL/TLS are no longer secure and may result in handshake failures. Consider upgrading to TLS 1.2 or TLS 1.3, which are currently the most secure versions.
- You need to install SSL/TLS certificates: Ensure that you have a valid SSL/TLS certificate installed on your web server. This certificate should be issued by a trusted certificate authority (CA) and should be valid for the domain name that you’re accessing. You can use online tools like SSL Checker to verify the validity of your SSL/TLS certificate.
- Wrong SSL/TLS configuration: Ensure that your SSL/TLS configuration is correct and matches the settings of your SSL/TLS certificate. If you’re using a self-signed certificate, make sure that you’ve installed it on your client device as well. If you’re unsure about your SSL/TLS configuration, you can use online tools like SSL Labs to diagnose any issues.
- Disable outdated SSL/TLS protocols: Disable outdated SSL/TLS protocols like SSLv3 or TLS 1.0/1.1 that are no longer secure. This will help prevent handshake failures and improve the security of your website. You can do this by modifying your web server configuration.
- Disable cipher suites with weak encryption: Disable cipher suites with weak encryption that are no longer secure. This will help prevent handshake failures and improve the security of your website. You can do this by modifying your web server configuration.
Troubleshooting and solution
When you set the system property javax.net.debug=ssl,handshake, it enables debug logging for SSL/TLS connections, including detailed information about the handshake process. This debug logging can help you determine whether a handshake failure is caused by the client or the server.:
javax.net.debug=ssl,handshake
Another option is to use JInfo to see SSL/TLS Properties of the Java process
The jinfo command allows to view the system properties of the process. For example, if the PID of your process is 12345, you can use the following command to view the system properties:
jinfo -sysprops 12345
This will output a list of all system properties set for your Java process, including the value of javax.net.ssl.trustStore.
Look for the javax.net.ssl.trustStore property in the output and verify that it’s set to the expected value. For example, if you expect the trustStore to be set to /path/to/truststore, look for a line in the output that looks like this:
javax.net.ssl.trustStore=/path/to/truststore
By using the jinfo command to verify the runtime system properties, you can ensure that your Java application or server is using the correct SSL/TLS trustStore at runtime.
A Test Class to check SSL Connectivity
Finally, you can try to reproduce the SSLHandshakeException locally programmatically. This can be useful, for example, if your Client application uses unsupported Cipher suites. By enabling them in your code, you can determine if that is the root cause.
Here is a sample Java Class you can use to test SSL Connectivity:
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class SSLConnectivityTest {
public static void main(String[] args) throws Exception {
// Set the SSL socket factory
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
// Set the SSL socket
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket("your.server.com", 443);
// Enable all supported cipher suites
String[] enabledCipherSuites = sslSocket.getSupportedCipherSuites();
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
// Start the SSL handshake
sslSocket.startHandshake();
// Get the input and output streams of the SSL socket
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
// Write data to the SSL socket
outputStream.write("Hello, server!\n".getBytes());
// Read data from the SSL socket
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = reader.readLine();
System.out.println("Server response: " + line);
// Close the SSL socket
sslSocket.close();
}
}
Make sure you are running the above class with the KeyStore/TrustStore System Properties:
java -Djavax.net.ssl.keyStore=path_to_keystore_file -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=path_to_truststore_file -Djavax.net.ssl.trustStorePassword=password SSLConnectivityTest
Using OpenSSL to rest a remote connection
Besides, you can also use openssl as Client to debug the remote connection to a secure Host. For example:
openssl s_client -debug -connect www.server.com:443
You can use the openssl tool to verify some use cases, such as if your Java Client is not sending the SNI (service Name Indication) extension to a SSL/TLS endpoint. You can add the SNI to the openssl tool to verify if this solves the issue as follows:
openssl s_client -debug -connect www.server.com:443 -servername www.server.com
Alternative approaches
In conclusion, this article was a walkthrough common issues you might face when connecting applications through a secure connection. It is worth mentioning some alternative approaches which can offload the complexity of your secure applications:
- Use Let’s Encrypt or ACME clients to automate certificate renewals, reducing expired cert issues.
- Migrate applications to TLS 1.3 where possible for better security and handshake performance.
- For APIs, use API gateways (Keycloak, Kong, NGINX) to handle TLS termination, offloading complexity from the JVM while maintaining secure connections.