Java 7 (acting as client) SSL handshake failure with keystore and truststore that worked in Java 6

stupor-mundi picture stupor-mundi · Aug 11, 2014 · Viewed 33.9k times · Source

I'm doing a JBoss AS 5.1 to 7.4, and Java 6 to 7 migration, and get a handshake failure.

The keystore and truststore are the ones we have been using successfully for ages with Java 6.

I've written some tests to narrow the problem down, it's definitely not JBoss but rather Java 7.

With SSL logging turned on, I get this:

17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) %% Invalidated:  [Session-2, SSL_RSA_WITH_RC4_128_SHA]
17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, WRITE: TLSv1 Alert, length = 2
17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, called closeSocket()
17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
17:44:30,041 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, called close()
17:44:30,042 INFO  [stdout] (http-/192.168.147.20:8080-120) http-/192.168.147.20:8080-120, called closeInternal(true)

There are some threads touching upon this (or a similar) problem, where people are suggesting to recreate certs or truststores with different params. I'd rather not go down this route, since I've recently without success tried to create more such keystores and truststores for different accounts of the same webservice.

Since we have been using these old (keystore and truststore) in production with Java 6, I'd like to keep them if at all possible.

It appears the problem may be caused by Java 7 being more tight regarding the checking of truststore certificate chain?

Is it possible to set some flags to relax the checking, make it behave like Java 6?

A thing I'm not 100% sure about is how to interpret the failure message: I think it's telling me that it's my machine (not the remove server), which isn't satisfied that the remote machine is safe. Is that correct?

Any help/ideas appreciated!

==========================================================

As suggested, have added PEM (with chain), exported from firefox when accessing the WS URL, to the truststore. This doesn't make it handshake, but slightly changes the failure.

***
%% Invalidated:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
main, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 2E                               .......
main, called closeSocket()
main, handling exception: 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
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 sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:804)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)

==============================================================

Also, as suggested in other threads, I've written another test that uses a TrustManager that does not validate certificate chains, and ran this with my original truststore.

This test is able to connect, and thus shows that my machine's validating of the remote machine is the only problem, and that my keystore is fine.

However, I can't use this approach for our actual webservice client, since that uses the Sun RPC lib, and connecting happens somewhere deep inside their code, so I can't touch it.

Answer

dave_thompson_085 picture dave_thompson_085 · Aug 11, 2014

First, yes, the exception says the Java SSL module in your machine doesn't trust the proof of identity (certificate) received from the server.

Yes, Java 7 does stricter checking. There may be more, but the one I'm sure of is that it doesn't allow the validity period of a child cert to end after the parent/CA cert (or begin before, but in practice that doesn't happen). See PKIX Path does not chain with any of the trust anchors error in Windows Environment which says it is a bug and will be fixed.

To check: if the server is a webserver, you could access any (harmless) page with a browser and use that to look at the cert chain. Otherwise, run openssl s_client -connect $host:443 -showcerts and once it connects enter EOF (Unix ^D, Windows ^Z), then put each ----BEGIN CERT... to -----END CERT... block in a different file and run openssl x509 -noout -subject -issuer -startdate -enddate on each in order.

To fix: if this is the problem, there doesn't seem to be any way to turn it off directly, except by turning off all cert checking (and thus losing some of the security of SSL), but adding the server entity cert to your truststore should work because then Java doesn't verify the chain. (You don't need to remove what's already there, just use an alias that isn't already in use.) Good luck.