Sometime back I got below error while connecting to SSL secured site in java. I was using java 8 update 60. Why this error comes and how to resolve it. We will see that.
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Before getting into solution, I would like go through how SSL Connection works.
- When client connects to web server secured with SSL, client will request server identity.
- Servers sends it identity along with it’s public key.
- Client validates server certificate against a list of trusted CAs and that the certificate is not expired, and that its common name is valid for the website that it is connecting to. If the client trusts the certificate, it creates, encrypts, and sends back a encrypted symmetric key using the server’s public key.
- Server sends back acknowledgement by sending message encrypting using client symmetric key.
- This is called SSL Handshake.
Now, let’s say, In java, client try to validates server certificate and certificate is not valid. it will throw below exception
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1351) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:156) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:925) at sun.security.ssl.Handshaker.process_record(Handshaker.java:860) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1043) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343) at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:728) at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123) at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:138) at SSLPoke.main(SSLPoke.java:31) Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382) … 15 more
This means client received invalid certificate which is not found in java trust store. (Java Trust Store File Location : /lib/security/cacerts)
For root cause identification,
- I open site in chrome and it worked and were getting valid server certificate.
- Then I tried to download certificate using openssl using below command,
openssl s_client -connect <hostname>:443
Output:
depth=0 CN = www.notexist.com verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 CN = www.notexist.com verify error:num=21:unable to verify the first certificate verify return:1 Certificate chain 0 s:/CN=www.notexist.com i:/CN=gaofang<br>Server certificate -----BEGIN CERTIFICATE----- <certificate_key> -----END CERTIFICATE----- subject=/CN=www.notexist.com issuer=/CN=gaofang
Checking that, I am getting CN (Common Name) as www.notexist.com which is obviously not valid. But howcome in browser, server sends right certificate. But if you import the certificate, it works fine. But who wants to import invalid certificate which might cause security issue.
Upon further investigation, we found this looks to be SNI (Server Name Indicator) issue. You can get more information on SNI at following site https://www.alibabacloud.com/help/faq-detail/43742.htm#concept-mrv-5wl-q2b
To verify, we ran below command
openssl s_client -servername <hostname> -connect <hostname>:443
This time we got the right certificate.
We got two solutions as below.
- Java 8 update 60 has support for it but client has to manually provide server name programatically. For this we would need to upgrade http client which supports it.
- Upgrade to Java > 8 update 191 (I tried with update 191.)
To probe certificates for a site, use following link. https://www.ssllabs.com/ssltest/analyze.html