9

Executive summary: how do I install a new root certificate into Java using Java code?

We have a desktop application which accesses various web services. Recently one of them switched their SSL certificate to one signed by Trustwave. While the Trustwave SSL certificates are accepted by regular internet browsers, Java does not seem to come with the prerequisite root certificates, and we lost access to the given web service with the following error message:

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

We got a temporary reprieve by convincing the provider to switch back to Verisign but when they switch back we have to be ready. So I need our desktop software to automatically install the Trustwave root certificate as needed. Our customers are not tech savvy enough to use the keytool command and I would rather not script it since that strikes me as a fragile solution (separate implementations for Mac and PC, the struggle against Vista execution restrictions, trouble finding the correct JRE to install into, etc).

I imagine the keytool uses Java internally. What command could I use within Java to replicate the functionality of keytool and install the root certificate programmatically?

Alexander Ljungberg
  • 6,302
  • 4
  • 31
  • 37

6 Answers6

2

I don't know if that is possible, but you could implement your own TrustManager to allow this connection or this CA. Here are the basics.

Community
  • 1
  • 1
Yishai
  • 90,445
  • 31
  • 189
  • 263
  • That could work for us. The link suggests a way to entirely disable verification which isn't what we want exactly but maybe I could sublcass the normal TrustManager to make one which tries all regular root certificates plus one. Thanks, this looks promising. – Alexander Ljungberg Oct 30 '09 at 17:55
1

If you want to install the certificate to the trusted root's keystore on the desktop machine, you will need permission to do that. It's the same with the keytool, you need a password to access the trusted root's keystore. If you want to be quick-n-dirty, you can

  • write the certificate to a file or a byte stream or whatever
  • import using KeyTool class (sun.security.tools.KeyTool)

But IMHO if the certificate is not valid, then it is not trustworthy. I would say there's a good reason for that.

Miguel Ping
  • 18,082
  • 23
  • 88
  • 136
  • It's valid according to Firefox and Internet Explorer. Java just comes with its own key store instead of using the OS provided one, and I suppose the Java version doesn't include the latest comers to the SSL market. – Alexander Ljungberg Oct 30 '09 at 16:18
  • There are cases, where this is reasonable: In our company, we use our own CA internally - naturally it's not trusted by default (browser etc. complain). Importing this CA certificate is much easier than adding exceptions for single certificates all over the place. – sfussenegger Oct 30 '09 at 16:21
1

IMHO, Sun has not exposed keytool via an API, primarily to prevent developers from modifying the set of trusted CAs. I can very imagine attackers exploiting such code to insert their own root certificates into the trust store compromising the very model of the trust store.

In fact, if you look at the source of the KeyTool class (sun.security.tools package), not only is it final, it also has a private constructor preventing any caller from creating an instance of the KeyTool class from code. KeyTool does have a main method, making the commandline (and hence an OS user) possibly the only manner in which one can initialize and communicate with KeyTool.

The only (simplistic) approaches left would be:

  • Initialize keytool as a process from the application, and pass commandline arguments to install the root CA certificate. This alone is a bad idea, and I would recommend notifying the user as to what is occuring.
  • Avoid the use of keytool and instead provide users with instructions on how to install the root CA using Keyman or KeyTool IUI. Speaking for myself only here, I prefer the latter.
Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • 1
    For the technical level of our users, automatic no questions asked installation is the only option that'd spare us from "your application is broken, fix it" kind of support questions. – Alexander Ljungberg Oct 30 '09 at 17:50
  • Regarding the philosophical question of whether applications should be allowed to modify the keystore the answer has to be of course -- otherwise the keytool itself couldn't work. Access to the operation could be controlled by a security manager perhaps. Alas, I digress. The design decisions behind this don't matter as much as the search for a practical solution. Thanks for the pointers either way. – Alexander Ljungberg Oct 30 '09 at 17:54
  • 3
    I had forgotten to mention another alternative - using your own trust store which could contain all the root CAs that you wish. You can get the JVM to use that truststore via the javax.net.ssl.trustStore property. – Vineet Reynolds Oct 30 '09 at 21:28
1

You could always invoke KeyTool as a process Runtime.exec(...).

Richard Perfect
  • 208
  • 2
  • 7
0

Command-line solution. On the Mac, the Java home is /Library/Java/Home. Try:

$ sudo -i
# cd /Library/Java/Home
# keytool -import -trustcacerts -alias CAName -file CA.crt -keystore lib/security/cacerts

Substitute CAName with the name of your CA, and CA.crt with a path to your certificate file (PEM works). It will prompt for a keystore password. The default password is given in the linked article.

I had to do this for one of RapidSSL's CA certs.

farcepest
  • 145
  • 6
  • I think my favorite part was where he said he wanted to do it without using keytool and you told him to use keytool ;) – Jeremy Logan Oct 30 '12 at 19:17
  • True. But I can't imagine writing a Java TrustManager and somehow distributing that app is easier than a scripted solution using keytool, even in a mixed environment. – farcepest Nov 07 '12 at 16:23
0

Sun published this code to create an updated version of your cacerts file based on any target host running https with any certs:

https://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java

Your new cacerts will be named jssecacerts in the current directory. Just copy that new file over your jre/lib/security/cacerts file.

I make no comment about the security of your new cacerts file.

notalbert
  • 91
  • 1
  • 2