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.
1 |
set-ReceiveConnector -Identity '[Server name]\Client Frontend MX1' -ProtocolLoggingLevel Verbose |
After this, I have logs. Here is what Exchange shows in the log:
1 2 3 4 5 6 7 8 9 |
[Removed for clarity],+,, [Removed for clarity],>,"220 [Exchange Hostname] Microsoft ESMTP MAIL Service ready at Wed, 5 Apr 2017 11:01:42 -0700", [Removed for clarity],<,EHLO [Application Hostname], [Removed for clarity],>,250 [Exchange Hostname] Hello [10.1.4.58] SIZE 37748736 PIPELINING DSN ENHANCEDSTATUSCODES STARTTLS AUTH GSSAPI NTLM 8BITMIME BINARYMIME CHUNKING, [Removed for clarity],<,STARTTLS, [Removed for clarity],>,220 2.0.0 SMTP server ready, [Removed for clarity],*," CN=[External Exchange Hostname] CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US [Certificate Serial Number] [External Exchange Hostname];[Exchange Hostname]",Sending certificate Certificate subject Certificate issuer name Certificate serial number Certificate thumbprint Certificate subject alternate names [Removed for clarity],*,,TLS negotiation failed with error CertUnknown [Removed for clarity],-,,Local |
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:
8 9 10 11 12 13 |
[12:20:58:244]|[04-05-2017]|[com.me.devicemanagement.framework.server.mailmanager.MailProcessor]|[INFO]|[114]|[a1e65a77-36d0-4b37-8f2e-24440c2f6cfe]: Caught exception in setting mail | javax.mail.MessagingException: Could not convert socket to TLS; nested exception is: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:1907) at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:666) |
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.
1 |
[Path to app folder]\jre\lib\security>..\..\bin\keytool -keystore cacerts -importcert -alias letsencryptroot -file e:\temp\letsencrypt_root.cer |
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.