Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
163 views
in Technique[技术] by (71.8m points)

java - Configure Spring Boot's with custom SSLContext programmatically (for mTLS)

Problem

Programmatically configure Spring Boot's to use my custom SSLContext. And use for mTLS.

Context

Spring's documentation provides only one, clear way how to configure SSL (via application.properties):

server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.trust-store=classpath:truststore.jks
...

However, this solution lacks depth as there are scenarios, where I'd like to advantage of customizing my own SSLContext. For example, configuring mTLS to trust not only single certificate generated via keytool, but to trust both my self-signed certificate and ones placed within Java's default TrustStore (lib/security/cacerts).

Of course, I could combine them using already mentioned keytool, but I'm looking for more flexible approach, hence providing my own SSLContext.

Spring provides a section on Configure the Web Server, which says to use either something like TomcatServletWebServerFactory or ConfigurableServletWebServerFactory, but they do not really go into depth.

I've tried creating a Component:

@Component
public class CustomServletCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    private static final String SERVER_CERTIFICATE_PATH = "identity.jks";
    private static final char[] PASSWORD = "secret".toCharArray();

    private static final String CLIENT_CERTIFICATE_PATH = "certs/client.cer";

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        factory.setSslStoreProvider(new SslStoreProvider() {
            @Override
            public KeyStore getKeyStore() throws Exception {
                var certificateAsInputStream = this.getClass().getClassLoader().getResourceAsStream(SERVER_CERTIFICATE_PATH);
                var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(certificateAsInputStream, PASSWORD);

                return keyStore;
            }

            @Override
            public KeyStore getTrustStore() throws Exception {
                var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(null);
                var organisationRootCertBytes = this.getClass().getClassLoader().getResourceAsStream(CLIENT_CERTIFICATE_PATH).readAllBytes();
                var certificateFactory = CertificateFactory.getInstance("X.509");
                var certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(organisationRootCertBytes));
                keyStore.setCertificateEntry("server", certificate);

                return keyStore;
            }
        });
    }
}

But to no avail.

question from:https://stackoverflow.com/questions/65890334/configure-spring-boots-with-custom-sslcontext-programmatically-for-mtls

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is unfortunately not possible with Spring Boot + Tomcat. It does not have an option to inject a SSLContext, or other properties such as SSLServerSocketFactory or TrustManager or KeyManager.

However if you still want to use Spring Boot and want to configure it fully and if you don't care what kind of server is used under the covers by Spring boot I would recommend Jetty.

Below is an basic implementation how you can accomplish this with Spring Boot + Jetty:

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;

import java.util.Collections;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public SslContextFactory.Server sslContextFactory() {
        SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
        sslContextFactory.setSslContext(sslContext);
        sslContextFactory.setIncludeProtocols(protocols);
        sslContextFactory.setIncludeCipherSuites(ciphers);
        sslContextFactory.setNeedClientAuth(true);
        return sslContextFactory;
    }

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory(SslContextFactory.Server sslContextFactory) {
        JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
        factory.setPort(8443);

        JettyServerCustomizer jettyServerCustomizer = server -> server.setConnectors(new Connector[]{new ServerConnector(server, sslContextFactory)});
        factory.setServerCustomizers(Collections.singletonList(jettyServerCustomizer));
        return factory;
    }

}

You also need to tell spring to not use tomcat anymore and switch to jetty. You can do that by adding the following snippet to your pom:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...