Search code examples
javasecuritysslkeystoretruststore

managing multiple entries in java (custom) truststore


I've read a few related questions, but none of them really helps me. So, let me give you a little background story: I'm writing a voting-system in which you have couple servers that manage registration, exchange of vote cards, and voting. During the registration user submits his personal data, the server checks if the data match those in the database and then accepts the user's certificate and adds it to its trustStore like this:

KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert);
trustKeyStore.setEntry(alias, entry, null);

and then stores the trustStore in the file. During the registration a lot of users register their certificates, and then they connect to a different server that requests both client and server authentication, and here the problem arises. I thought that this 'other server' can use the aforementioned trustStore to perform the SSLHandshake with users, but it appears that the trustStore 'remembers' only the last user that registered. I'm creating SSLServerSocket like this:

    tmf.init(trustKeyStore);
    kmf.init(keyStore, password);

    // create, init SSLContext
    SSLContext sslCtx = SSLContext.getInstance("TLS");
    sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    // create SSLSocketFactory and SSLSocket
    SSLServerSocketFactory sslSocketFactory =   (SSLServerSocketFactory)sslCtx.getServerSocketFactory();
    sslSocket = (SSLServerSocket)sslSocketFactory.createServerSocket(1234);

As I mentioned before, both-side authentication works fine, but only for the last entry in the trustStore. So, now finally my question is - do I have to create a separate trustStore for each user? Do I have to overload TrustManager? Basically, is there any way I can make the SSLContext/SSLEngine iterate over all of my trusted certificates and go through with the handshake if it finds the one that matches?

UPDATE

I think I need to clarify a few things. First of all, this is not a web application, just a normal client-server java swing app. Every single certificate used (server or client) is self-signed, but the server's cert is build into the client application, so when the client connects to the server he can compare certificates (works). I have also tried to solve it exactly like Bruno suggested - after user registers, server checks if his data is valid, if it is it issues a new certificate for the client, adds it to his truststore and sends it to the client. But that didn't work even for the last registered client.


Solution

  • I have solved the problem. As it often happens, there was a stupid mistake on my side - one of the servers were overwriting the whole keystore. I have spent a lot of time figuring how to set up this ssl communication and I haven't found much about it on the web, so I hope this will help somebody in the future.

    In order to set up server-only communication you need to do the following: 1. On the client side obtain (somehow, in my case it was built in the client app) the server's certificate and add it to your truststore like that:

            KeyStore clientTrustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            clientTrustedKeyStore.load(null, "password".toCharArray());
            KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert);
            clientTrustedKeyStore.setEntry("alias", entry, null);
    
    1. While creating sslsockets either on the client or the server side, you need to 'feed' the SSLContext with your keyStore (server), or trustStore (client) to the SSLContext:

      tmf = TrustManagerFactory.getInstance("SunX509");
      kmf = KeyManagerFactory.getInstance("SunX509");
      tmf.init(trustKeyStore);
      kmf.init(keyStore, password);
      
      // create, init SSLContext
          SSLContext sslCtx = SSLContext.getInstance("TLS");
          sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
      

    and then create the sslsocketfactory and sockets.

    1. After creating sockets (only useful on the server side) set authentication mode:

      sslSocket.setNeedClientAuth(boolean);

    In case of setting up the both-side verification, things you have to change are: authentication mode (obviously), adding certificates to the trustStores on both client and server sides.

    The other issue with both-side verification is that the below scenario won't work (although, at least for me, it seems logical): 1. Server issues its own self-signed certificate, adds it to its trustStore and then sends it to the client for him to authenticate with during future connections. Why won't it work? Because when the client gets the certificate he can only add it to his keystore like that:

    ks.setCertificateEntry(alias,cert);
    

    whichjust won't do when it comes to SSLHandshake, you have to authenticate yourself with the certificate added to your keystore with setEntry:

    ks.setKeyEntry(alias,keyPair.getPrivate(),keyStorePassword,certChain);
    

    where certChain is i.e.

    Certificate[] certChain = {myCert};
    

    That would be all, I'm sure for some people all this is quite obvious, but I hope it will help some SSL beginners like myself :)