truststore in jar?

T

Tim Ward

Scenario: Java application distributed as a single jar file. It now needs to
make SSL connections where it used to make TCP connections. So it needs a
truststore so as to be able to validate the certificate presented by the
server.

I've got all that working fine with the truststore in a separate disk file,
but that's no use for distributing the application. So, if I include the
truststore in the jar file (along with the images and other resource files)
how do I make the Java SSL system use it?

(The only suggestion I've found is to include it as a resource, then write
some code to copy this to a disk file before setting the system property.
This is nonsense, mostly because it is obviously utterly silly but also
because it's less than obvious that you can guarantee to find somewhere to
put the file safely and reliably.)
 
E

EJP

Tim said:
So, if I include the
truststore in the jar file (along with the images and other resource files)
how do I make the Java SSL system use it?

You can do it by getting a javax.net.ssl.SSLContext via
SSLContext.getInstance, then call SSLContext.init(). The second
parameter is an array of TrustManagers. You can define your own
TrustManager to load your truststore from wherever you like, using in
your case java.security.KeyStore.load() from your truststore resource's
input stream, and implement the X509TrustManager API to respond
appropriately when SSL asks you any of those questions by looking in
that KeyStore. You then use that SSLContext to obtain your SSL socket
factories, use them to get your SSLSockets, and away you go.

However having said all that I would probably recommend that you rethink
the strategy instead. What happens when your server certificate expires?
Another software distribution? What about those clients you aren't able
to reach with your distribution any more, for whatever reason? do you
really want to cut them off at the knees?

All your clients really need in their truststores is a root certificate
to which your server certificate leads, and actually Java's truststore
should already have one of those. It ships with lots of root certs for
Verisign, CyberTrust, Equifax, Baltimore,Entrust, GeoTrust, Thawte, &c,
and your certificate is almost certainly signed by someone whose cert
chain leads back to one of those.
 
T

Tim Ward

EJP said:
You can do it by getting a javax.net.ssl.SSLContext via
SSLContext.getInstance, then call SSLContext.init(). The second
parameter is an array of TrustManagers. You can define your own
TrustManager ...

I was beginning to think that was probably the way forward ... thanks for
confirming it. (Before I'd written lots of code trying out various wrong
approaches.)
However having said all that I would probably recommend that you rethink
the strategy instead. What happens when your server certificate expires?
Another software distribution?

Well, not when any individual server certificate expires, only when the
private root certificate is compromised ... in which case another
distribution of my little application will be the least of my client's
worries.
What about those clients you aren't able
to reach with your distribution any more, for whatever reason? do you
really want to cut them off at the knees?

Not my decision, I just implement the spec. But the answer could well be
something like: "yes, cutting them off is fine, because if they can't be
reached for a software distribution they probably also can't be reached to
be billed for their support contract".
All your clients really need in their truststores is a root certificate
to which your server certificate leads, and actually Java's truststore
...ships with lots of root certs ...
and your certificate is almost certainly signed by someone whose cert
chain leads back to one of those.

Nope, it's a private certificate hierarchy.

Thanks again.
 
T

Tim Ward

EJP said:
You can do it by getting a javax.net.ssl.SSLContext via
SSLContext.getInstance, then call SSLContext.init(). The second
parameter is an array of TrustManagers. You can define your own
TrustManager to load your truststore from wherever you like, using in
your case java.security.KeyStore.load() from your truststore resource's
input stream, and implement the X509TrustManager API to respond
appropriately when SSL asks you any of those questions by looking in
that KeyStore. You then use that SSLContext to obtain your SSL socket
factories, use them to get your SSLSockets, and away you go.

.... except that I don't want to try to understand how to write code to
"implement the X509TrustManager API" because all that code already exists: I
don't want any different algorithms to what's built in, only different data.

Instead, why can't I:

(1) get a TrustManagerFactory
(2) make a KeyStore and load it from the resource as you say
(3) use this KeyStore to init() the TrustManagerFactory
(4) get the TrustManagerFactory to make me a TrustManager
(5) pass this to SSLContext.init() and continue as you say

ie, instead of writing code to implement X509TrustManager, get one from a
customised TrustManagerFactory?

In which case, all I need to know is which version of
TrustManagerFactory.instance() to call and what parameters to pass it in
order to obtain a TrustManagerFactory that will behave in exactly the same
way as the default one except that it's got a different set of trusted root
certificates? Ah, probably I just pass it getDefaultAlgorithm()?
 
R

Rogan Dawes

Tim said:
... except that I don't want to try to understand how to write code to
"implement the X509TrustManager API" because all that code already exists: I
don't want any different algorithms to what's built in, only different data.

Instead, why can't I:

(1) get a TrustManagerFactory
(2) make a KeyStore and load it from the resource as you say
(3) use this KeyStore to init() the TrustManagerFactory
(4) get the TrustManagerFactory to make me a TrustManager
(5) pass this to SSLContext.init() and continue as you say

ie, instead of writing code to implement X509TrustManager, get one from a
customised TrustManagerFactory?

In which case, all I need to know is which version of
TrustManagerFactory.instance() to call and what parameters to pass it in
order to obtain a TrustManagerFactory that will behave in exactly the same
way as the default one except that it's got a different set of trusted root
certificates? Ah, probably I just pass it getDefaultAlgorithm()?

You may find the following article interesting:

<http://www.devx.com/tips/Tip/30077>

Obviously you don't need the GUI. But it shows a pretty minimal
implementation of an X509TrustManager

Regards,

Rogan
 
E

EJP

Tim said:
Instead, why can't I:

...

ie, instead of writing code to implement X509TrustManager, get one from a
customised TrustManagerFactory?

...

This all sounds OK, stupid of me not to think of it.
 
Joined
Jul 22, 2006
Messages
1
Reaction score
0
not working for me

I'm trying to do this very thing, but I still get the dreaded javax.net.ssl.SSLHandshakeException. Here's a snippet of my code. Am I interpreting you correctly?

Code:
// just to be sure
System.clearProperty("javax.net.ssl.keyStore");

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = TestCreateSchedule.class.getResourceAsStream("/myTrust.jks");
keystore.load(keystoreStream, null);
trustManagerFactory.init(keystore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustManagers, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

// using commons-httpclient to test
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://serverurl:8002/path/to/service");
httpclient.executeMethod(httpget);

I've verified that it is correctly reading myTrust.jks from the jar file, and that I can make a connection to https://serverurl:8002/path/to/service if I set the javax.net.ssl.keyStore system property to point to the keystore in the filesystem. Am I missing something?

Here's the stack trace:

Code:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.Su
nCertPathBuilderException: unable to find valid certification path to requested target
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(Unknown Source)
        at com.sun.net.ssl.internal.ssl.AppOutputStream.write(Unknown Source)
        at org.apache.commons.httpclient.HttpConnection$WrappedOutputStream.write(HttpConnection.java:1368)
        at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
        at java.io.BufferedOutputStream.flush(Unknown Source)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:799)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2277)
        at org.apache.commons.httpclient.HttpMethodBase.processRequest(HttpMethodBase.java:2657)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1093)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:675)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:529)
 
Last edited:
Joined
Mar 19, 2007
Messages
1
Reaction score
0
Hi,

You should try this :
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = ClassLoader.getSystemResourceAsStream("myTrust.jks");
keystore.load(keystoreStream, "myPassword".toCharArray());
trustManagerFactory.init(keystore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustManagers, null);
SSLContext.setDefault(sc);

It works on my side with the axis librairy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top