Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update Truststore dynamically?

I have currently implemented mutual TLS in my Spring Boot application and I am doing it programmatically, like so:

@Bean
public ServletWebServerFactory servContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    TomcatConnectorCustomizer tomcatConnectorCustomizer = new TomcatConnectorCustomizer() {
        @Override
        public void customize(Connector connector) {
            connector.setPort(8443);
            connector.setScheme("https");
            connector.setSecure(true);
            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();

            protocol.setSSLEnabled(true);
            protocol.setKeystoreType("PKCS12");
            protocol.setKeystoreFile(keystorePath);
            protocol.setKeystorePass(keystorePass);
            //client must be authenticated (the cert he sends should be in our trust store)
            protocol.setSSLVerifyClient(Boolean.toString(true));
            protocol.setTruststoreFile(truststorePath);
            protocol.setTruststorePass(truststorePass);
            protocol.setKeyAlias("APP");
        }
    };
    tomcat.addConnectorCustomizers(tomcatConnectorCustomizer);
    return tomcat;
}

This is working fine and as expected, but I have a requirement where I need to update the trust store during runtime (for example, when a @getmapping endpoint is invoked).

Specifically, I need to add a new certificate to the TrustStore without stopping/restarting the application. So I will have to somehow modify the in-memory trust store of my application.

How can I do this?

I tried to add a bean dynamically which adds a new Trust Manager to the SslContext, but this does not work.

@GetMapping("/register")
public String Register() throws Exception {
    ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) appContext;
    ConfigurableListableBeanFactory beanRegistry = configContext.getBeanFactory();
    SSLContext sslContext = getSSLContext();
    beanRegistry.registerSingleton("sslContext", sslContext);
    return "okay";
}


public  SSLContext getSSLContext() throws Exception {
    TrustManager[] trustManagers = new TrustManager[] {
            new ReloadableX509TrustManager(truststoreNewPath)
    };
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustManagers, null);
    SSLContext.setDefault(sslContext);
    return sslContext;
}

I also tried to invoke the above getSSLContext() as a @bean, which did not work either.

My current solutions are based on these links, which are for Java, but I'm not sure how to implement them in my Spring application.

  • https://jcalcote.wordpress.com/2010/06/22/managing-a-dynamic-java-trust-store/

I have found a solution which describes exactly how to have a dynamic trust store, but I am not able to figure out how to reload the trust store during runtime. Say, for example, when a GET endpoint is invoked.

Client Certificate authentication without local truststore I have a list of Certificates, I just need to know how to invoke the ReloadableX509TrustManager's addCertificates() method.

like image 250
Sapnesh Naik Avatar asked Jul 22 '19 10:07

Sapnesh Naik


1 Answers

First, make your ReloadableX509TrustManager a managed bean - eg annotate with @Component

@Component
class ReloadableX509TrustManager 
    implements X509TrustManager {
.....
    public ReloadableX509TrustManager(@Value("someValueFromAppConfig")String tspath){....}
.....

Second, use it in your controller instead of creating new one eg

@GetMapping("/register")
public String Register() throws Exception {
    ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) appContext;
    ConfigurableListableBeanFactory beanRegistry = configContext.getBeanFactory();
    SSLContext sslContext = getSSLContext();
    beanRegistry.registerSingleton("sslContext", sslContext);
    return "okay";
}

@Autowired private ReloadableX509TrustManager reloadableManager;
public  SSLContext getSSLContext() throws Exception {
    TrustManager[] trustManagers = new TrustManager[] {
            reloadableManager
    };
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustManagers, null);
    SSLContext.setDefault(sslContext);
    return sslContext;
}

Thrid, follow article to get to know how to "reload" that trust manager. It can be done by changing most of methods to package protected and invoke it from some sort of sertificate service - or make it public and call directly. The choice is yours.

like image 185
Antoniossss Avatar answered Oct 12 '22 23:10

Antoniossss