Java Does Not Trust Me

Do you have an application that uses Java, or comes with a JRE included, and do you use self-signed certificates, or even certificates from Let’s Encrypt? You might have issues, and the cause may not be all that obvious.

At work have a systems management application that uses a built-in copy of the JRE (Java Runtime Environment). This application during the course of normal operations (or, especially during abnormal operations) send emails to administrators, keeping them informed of overall system health, etc. When you first set this application up, it will not accept setting for an email server unless it is able to successfully send a test message. I understand this is so you cannot “set it and forget about it”, only to later realize that it never worked properly. It is mildly annoying that you end up retyping the login details a hundred times in the course setup when something doesn’t work right off the bat though, but I digress…

Our mail server is Microsoft Exchange 2016. The default SMTP connector for clients runs on TCP port 587, and requires TLS before you can authenticate using the LOGIN method (username and password send in base-64 encoding). The application in question allows you to configure all this stuff: host name, port, use TLS, authentication required, etc. Seemingly, everything checked out okay, but every time you hit save and the test runs, immediate failure. In fact, it failed so fast you would swear it wasn’t even checking anything. The exchange connector logs were not even showing anything.

The first issue was that the default “Client Frontend [Server name]” receive connector didn’t have logging turned on. Thus the no logs situation. Thanks Microsoft.

After this, I have logs. Here is what Exchange shows in the log:

Line 8 is the first clue (“TLS negotiation failed with error CertUnknown”). The certificate is issued for Let’s Encrypt and has the external hostname as the certificate subject, and both the external and internal hostnames as subject alternative names. Exchange is passing the correct cert, so time to check the other side of the connection. The logs from the application follow:

The magic words here are “unable to find valid certification path to requested target”. As it turns out, the keystore that java uses for what root certificates are trusted included with the app does not include the root CA certificate that is used by Let’s Encrypt. To fix this you need to export the root CA certificate and import it.

You also need the password to the keystore, but in my case is was the default of changeme.

After importing the cert, connection worked and the mail flowed.

TL;DR – JRE may not have the CA root of your cert. Import it to make it connect.