0xF1
0xF1

Reputation: 6116

How to add multiple truststore paths to “java.net.ssl.trustStore”?

I want my Java Code to search for CA certificate of the server in one keystore... if it is unable to find the specific certificate (which I think will be known only when I try to connect via LDAP to Directory Server), it should look for the certificate in another keystore, whose path I know.

I tried this:

System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + "/lib/security/cacerts" + System.getProperty("path.separator") + path/to/second/keystore);

But it didn't seem to work.

Adding only one path (either of them) works, i.e. it runs like charm if certificate is found and fails if not.

So my question is:

  1. Is there a method to add multpile keystore paths to javax.net.ssl.trustStore?

  2. If it is not possible how should I write my code (I am asking for the algorithm) so that it just not throw Exception after first search itself and fail?

P.S. : I am not much familiar with Java.

Below is the relevant section of my code:

if(useSSL)
{
  try 
  {   
    SSLContext se = SSLContext.getInstance("TLS");
    Security.addProvider(se.getProvider());
  }   
  catch(NoSuchAlgorithmException e) { }

  System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + "/lib/security/cacerts");

  com.org.ldap.LDAPSocketFactory ssf = new LDAPJSSESecureSocketFactory();
  LDAPConnection.setSocketFactory(ssf);
}

try 
{   
  lc = new LDAPConnection();
  lc.connect( ldapServer, ldapPort);
  lc.bind( ldapVersion,  ldapUser, (userInfo[1]).getBytes() );
}
catch (LDAPException le)
{
  le.printStackTrace();
}

Upvotes: 25

Views: 42312

Answers (3)

Hakan54
Hakan54

Reputation: 3871

Although it is a bit late, I would like to make a remark on the answer which the others have provided. It was nearly impossible indeed and with a lot of custom code it would be possible actually, we have seen it within an answer of a similar question before: Using a custom truststore in java as well as the default one

After encountering the same challenge for multiple projects I thought it would be handy to create a library and also make it publicly available to contribute back to the community. Please have a look here: Github - SSLContext-Kickstart

The usage for your use case would be for loading the jdk truststore and your own truststore:

import nl.altindag.sslcontext.SSLFactory;

import javax.net.ssl.SSLContext;
import java.security.cert.X509Certificate;
import java.util.List;

public class App {

    public static void main(String[] args) {
        String trustStorePath = ...;
        char[] password = "password".toCharArray();


        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .withTrustMaterial(trustStorePath, password)
                .build();

        SSLContext sslContext = sslFactory.getSslContext();
        List<X509Certificate> trustedCertificates = sslFactory.getTrustedCertificates();
    }

}

Upvotes: 2

user207421
user207421

Reputation: 310903

No, just import all the certificates from one truststore into the other, and use the second.

Upvotes: 3

Bruno
Bruno

Reputation: 122649

You can't have multiple paths for javax.net.ssl.trustStore.

The easiest would be to make a local copy of the JRE's cacerts and import the certificates from your other store into it (effectively merging them). (See keytool -importkeystore.)

Otherwise, if you know in advance that all your LDAP connections will use your second keystore (and you also want to be able to use the default truststore for other, non-related connections), you could configure that trust store for that SSLSocketFactory only. I'm not familiar with com.org.ldap.LDAPSocketFactory, but it might have an option to do so. (Otherwise, you could create your custom SSLContext initialised with your second truststore and get an SSLSocketFactory, as described in this answer).

Another, more complicated way, would be to create a custom X509TrustManager that wraps the default trust manager, catches its exceptions and tries again with another trust manager initialised using your second store. It's feasible, but you'd need to make sure it still throws the exception if neither trust managers accept your certificate (otherwise, there would be a security hole). If you're not familiar with the JSSE API (or Java altogether), it's probably not the best option.

In addition, be careful when you use System.setProperty("javax.net.ssl.trustStore", ...) in your code: it is what's read to initialise the default SSLContext, but the default SSLContext is only initialised once, the first time it's required. Setting this system property afterwards would have no effect (unless of course, other classes from other libraries also rely on this value).


It's also not clear what you're trying to achieve with this, since you'll always ever succeed to add a security provider that's already there:

  try 
  {   
    SSLContext se = SSLContext.getInstance("TLS");
    Security.addProvider(se.getProvider());
  }   
  catch(NoSuchAlgorithmException e) { }

Upvotes: 21

Related Questions