The error you reported indicates that your application is unable to establish a trusted SSL connection with the remote peer, because it is unable to find a valid certification path.
Which seems very strange to me is why it worked a few days ago and now it is not: perhaps the server changes the certificate, or maybe your setup change in some way.
The SSL configuration will be highly dependent on the software you are using to connect with the remote server: it can be different if you are using standard Java classes like URLConnection
or HttpURLConnection
, or libraries like Apache HttpClient or OkHttp, among others. The difference mainly has to do with if that piece of software uses or not Java Secure Socket Extension (JSSE) under the hood.
Assuming that you are using JSSE, in order to successfully configure your trust relationship, you need to properly configure a TrustManager
, and more specifically, an X509TrustManager
. From the docs:
The primary responsibility of the TrustManager
is to determine whether the presented authentication credentials should be trusted.
Basically you can configure this X509TrustManager
in two ways.
On on hand, you can create your own implementation. For example:
// This KeyStore contains the different certificates for your server
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(
new FileInputStream("/path/to/serverpublic.keystore"),
"yourserverpublickeystorepassword".toCharArray()
);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // SunX509
tmf.init(keyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
// You can configure your client for mutual authentication
// and/or provide a SecureRandom also if you wish
sslContext.init(null, trustManagers, null);
Please, consider read this SO question for a complete example.
Or, on the other hand, as you are doing, you can configure the standard TrustManagerFactory
properly.
As indicated in the above-mentioned documentation, the standard TrustManagerFactory
uses the following process to try to find trust material, in the specified order:
- First, you can use the
javax.net.ssl.trustStore
system property to point to the keystone that contains your trusted server certificates when running the application. If the javax.net.ssl.trustStorePassword
system property is also defined, then its value is used to check the integrity of the data in the truststore before opening it.
- If the
javax.net.ssl.trustStore
system property was not specified, then:
- if the file
java-home/lib/security/jssecacerts
exists, that file is used;
- if the file
java-home/lib/security/cacerts
exists, that file is used;
- if neither of these files exists, then the SSL cipher suite is anonymous, does not perform any authentication, and thus does not need a truststore.
No matter the chosen mechanism used, you must be sure that the keystore contains all the necessary certificates to trust the remote server, not only the SSL certificate, but all the certificates in the certificate chain.
openssl
provides an useful command that allows you to obtain all the certificates used in the SSL connection:
openssl s_client -showcerts -connect google.com:443
Of course, modify the domain as appropriate.
It will output different certificates; be sure to save each of them, including —–BEGIN CERTIFICATE—–
and —–END CERTIFICATE—–
, and include them in your keystore.
This handy utility can probably be of help for this purpose as well: as indicated in the project README,
it basically will try to connect to the remote peer and save the necessary certificate information. This related article provides more information about the tool.