3

I have an existing application that uses the org.restlet.engine.ssl.DefaultSslContextFactory and a keystore file that is loaded when the server is started. I have another application that creates certificates that have to be added dynamically to the keystore file while the server is running. To perform this, I created the certificate and the private key in the code and then write it to a directory. The directory is watched by a bash script that checks for new files, and if one appears it will be imported into the existing keystore file.

But when trying to access the server with the newly imported certificate the handshake fails. Only when restarting the server, the access can be completed successfully, which I assume means that the added certificate will not be reloaded by the server.

Is there a way to update the running application with the new entry in the keystore file?

nCessity
  • 735
  • 7
  • 23
wasp256
  • 5,943
  • 12
  • 72
  • 119
  • Have you had a look to this answer ? http://stackoverflow.com/questions/859111/how-can-i-use-different-certificates-on-specific-connections – Tom Feb 08 '17 at 15:14

3 Answers3

0

Importing the new certificate into your keystore doesn't refresh your current SSLConext as nothing tells the JVM that the keystore has changed.

To do that you'll have to tell your application that a new certificate was added into your keystore but instead of reloading the keystore -as far as I know it shouldn't be possible- , what is possible by the way is that your can add the new certificate into your current SSLContext See here.

To achieve that, you've to provide a bean aware of the new certificate -maybe the component that call your bash script- in which you injected an SSLContext instance.

It's also interesting if you split your application using a micro service architecture, delegate to one module the fact to deal with certificates and reload it (using proper configure LB) as keystore is updated.

Abderrazak BOUADMA
  • 1,526
  • 2
  • 16
  • 38
  • I don't get what you mean with the last paragraph!? The bash script is running by itself,, so where exactly would i inject the instance? – wasp256 Feb 02 '17 at 16:09
  • Into your Java code, it's up to your code to refresh in memory certificate chain – Abderrazak BOUADMA Feb 03 '17 at 06:19
  • I don't think so as if it was possible to automatically reload a keystore (or cacerts) through the JVM that would had been a serious security breach. My solution is based on the fact that you'll have to develop your own `TrustManager` mechanisme replacing the actual one (which BTW a hard thing to do and maintain) or maybe think of split your application into smaller parts (micro services) and reload the secured module as certificates are updated – Abderrazak BOUADMA Feb 03 '17 at 10:21
0

Since this seems to be quite an impossible task to accomplish, I decided to do a workaround. I used nginx as a proxy in front of the application. Nginx is able to perform client authentication with multiple CA root certificates which is exactly what I need. The connection between the application and nginx can simply be done via a HTTP since they are residing on the same host (just different ports).

wasp256
  • 5,943
  • 12
  • 72
  • 119
0

Ugly Hack Alert

The following works for me. I overwrite DefaultSSLContextFactory, store each created SSLContext and call its init method again.

To invoke the reload I simply call UglyHackSSLContextFactory.reloadKeyStore()

package test;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;

import javax.net.ssl.SSLContext;

import org.restlet.engine.ssl.DefaultSslContextFactory;

public class UglyHackSSLContextFactory extends DefaultSslContextFactory {

    private SSLContext _context = null;
    public static ArrayList<UglyHackSSLContextFactory> instances = new ArrayList<UglyHackSSLContextFactory>();

    // we need all used SSLContextFactories later, so store them
    public UglyHackSSLContextFactory() {
        instances.add(this);
    }

    // create a new context once and store it.
    @Override
    public SSLContext createSslContext() throws Exception {
        if (this._context == null) {
            this._context = super.createSslContext();
        }
        return this._context;
    }

    // re-init all _context instances
    public static void reload() throws KeyManagementException, UnrecoverableKeyException,
            KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
            CertificateException, IOException {
        for (final UglyHackSSLContextFactory f : instances) {
            f.reinit();
        }
    }

    // this is mostly copied from
    // org.restlet.engine.ssl.DefaultSslContextFactory
    private void reinit() throws KeyManagementException, KeyStoreException,
            NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException,
            UnrecoverableKeyException {

        javax.net.ssl.KeyManagerFactory kmf = null;

        if ((getKeyStorePath() != null) || (getKeyStoreProvider() != null)
                || (getKeyStoreType() != null)) {
            // Loads the key store.
            final KeyStore keyStore = (getKeyStoreProvider() != null) ? KeyStore.getInstance(
                    (getKeyStoreType() != null) ? getKeyStoreType() : KeyStore.getDefaultType(),
                    getKeyStoreProvider()) : KeyStore
                    .getInstance((getKeyStoreType() != null) ? getKeyStoreType() : KeyStore
                            .getDefaultType());
            FileInputStream keyStoreInputStream = null;

            try {
                keyStoreInputStream = ((getKeyStorePath() != null) && (!"NONE"
                        .equals(getKeyStorePath()))) ? new FileInputStream(getKeyStorePath())
                        : null;
                keyStore.load(keyStoreInputStream, getKeyStorePassword());
            } finally {
                if (keyStoreInputStream != null) {
                    keyStoreInputStream.close();
                }
            }

            // Creates the key-manager factory.
            kmf = javax.net.ssl.KeyManagerFactory.getInstance(getKeyManagerAlgorithm());
            kmf.init(keyStore, getKeyStoreKeyPassword());
        }

        javax.net.ssl.TrustManagerFactory tmf = null;

        if ((getTrustStorePath() != null) || (getTrustStoreProvider() != null)
                || (getTrustStoreType() != null)) {
            // Loads the trust store.
            final KeyStore trustStore = (getTrustStoreProvider() != null) ? KeyStore
                    .getInstance(
                            (getTrustStoreType() != null) ? getTrustStoreType() : KeyStore
                                    .getDefaultType(), getTrustStoreProvider()) : KeyStore
                    .getInstance((getTrustStoreType() != null) ? getTrustStoreType() : KeyStore
                            .getDefaultType());
            FileInputStream trustStoreInputStream = null;

            try {
                trustStoreInputStream = ((getTrustStorePath() != null) && (!"NONE"
                        .equals(getTrustStorePath()))) ? new FileInputStream(getTrustStorePath())
                        : null;
                trustStore.load(trustStoreInputStream, getTrustStorePassword());
            } finally {
                if (trustStoreInputStream != null) {
                    trustStoreInputStream.close();
                }
            }

            // Creates the trust-manager factory.
            tmf = javax.net.ssl.TrustManagerFactory.getInstance(getTrustManagerAlgorithm());
            tmf.init(trustStore);
        }

        SecureRandom sr = null;

        if (getSecureRandomAlgorithm() != null) {
            sr = SecureRandom.getInstance(getSecureRandomAlgorithm());
        }

        this._context.init(kmf != null ? kmf.getKeyManagers() : null,
                tmf != null ? tmf.getTrustManagers() : null, sr);

    }

}

Hope this helps!

nCessity
  • 735
  • 7
  • 23