Java – Query on jvm truststore and jssecacerts file

httpsjavassl

i have two https web applications app1 and app2 installed on two different tomcats t1 and t2(t1 and t2 are on different machines). when in app1
i make an url connection to app2 i get the SSL handshake error. The reason is i am using the self signed certificate in app2 which is not present
in app1 jvm truststore. So proper approach to fix it install the self signed certificate in JAVA-HOME/jre/lib/security . To do the
same , I have followed the steps given at http://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/.
Same steps are suggested across the different forums. But still i get the same SSL handshake error which 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.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)

Though it i got rid of this SSLHandshakeException by mentioning below parameters in JVM trustore.
-Djavax.net.ssl.trustStore=C:.keystore
-Djavax.net.ssl.trustStorePassword=changeit

My question here is why first approach(which is proper approach) i.e putting the jssecacerts file under /lib/security is not working?
Another point is what is different between first and second approach ?

Best Answer

Though it i got rid of this SSLHandshakeException by mentioning below parameters in JVM trustore. -Djavax.net.ssl.trustStore=C:.keystore -Djavax.net.ssl.trustStorePassword=changeit

It's not clear what you were trying to do with these options. Either you use the default truststore (normally jssecacerts, if it exists; otherwise, cacerts) or you specify your own. .keystore tends to be used as a keystore, not truststore (although there is no default JSSE value). (I would also specify the full path instead of C:.keystore, by the way.)

It's probably better to make a copy of your original cacerts (or jssecacerts) file (remove the extra one you've put, if you've changed something) and add your remote certificate to it (i.e. app2's cert in app1's copy and app1's cert in app2's copy, if needed).

You can list the certificates using keytool -list -keystore keystore.jks (see help for more options if needed).

You can export a certificate using keytool -export -keystore server1-keystore.jks -alias server_alias -file server1.crt.

Then, import it in the other truststore: keytool -import -keystore client2-truststore.jks -file server1.crt. (Here, client2-truststore.jks will be a copy of cacerts.) Then, configure your JVM running Apache Tomcat (not necessarily the Tomcat connector) to use it. You should be able to set the JVM parameters in catalina.sh (JAVA_OPTS=-D...).

EDIT:

My question here is why first approach(which is proper approach) i.e putting the jssecacerts file under /lib/security is not working?

To answer your question more directly, I've just double-checked on a clean Oracle JRE 6 installation (1.6.0_31), and jssecacerts takes precedence over cacerts when present (as documented in the JSSE Ref Guide, so there doesn't seem to be a bug). I'm not sure where Oracle have moved Andreas Sterbenz's Sun blog, so I'm not sure which copy of InstallCert you've used. I guess something my have gone wrong there.

As far as I'm aware, InstallCert connects to the server to get its certificate (replacing the export step above): you effectively assume that the certificate you get on your first connection is the right one (and can be trusted). You could also get that certificate using OpenSSL. However, in your case, you seem to have control over the two servers and their respective keystores, so you might as well use keytool -export to be sure.

Another point is what is different between first and second approach ?

The first approach (changing jssecacerts) sets the configuration for all applications that will use this installation of the JRE, whereas the second will apply those settings to the JVM once it's running Apache Tomcat only.

Note, that if you didn't have a jssecacerts but only a cacerts file, if you only import your certificate into jssecacerts, cacerts will be ignored, so you won't be able to connect to servers that have a certificate issued by a CA that would normally be trusted by default. That's why starting with a copy of the default file can be useful. (In addition, if your application also connects to other sites that would normally be trusted by default, this could also explain why you'd get this error message, at a different place this time.)

Ultimately, it's your responsiblity to check what's in jssecacerts or cacerts:

IMPORTANT NOTE: The JDK ships with a limited number of trusted root certificates in the /lib/security/cacerts file. As documented in keytool, it is your responsibility to maintain (that is, add/remove) the certificates contained in this file if you use this file as a truststore.

Depending on the certificate configuration of the servers you contact, you may need to add additional root certificate(s). Obtain the needed specific root certificate(s) from the appropriate vendor.