1

I need to make sure that my client supports SNI and it works. To do that, I want to start some dummy server (Spring boot application with it's embedded Tomcat 9) that requires hostname from client. The question is how to force Spring boot embedded Tomcat to require SNI from client? I've read that to do that, we need to provide to server 2 (or more) certificates.

I tried to follow example from this topic. But it does not work for me.

I generated 2 keystores mycompany1.keystore and mycompany2.keystore with 1 certificate in each of them. And 1 truststore mycompany.truststore that stores both these certificates. Certificates have CN my.hostname.com and my.another.hostname.com.

This is the way how I configure embedded tomcat:

@Component
public class MultipleHostsTomcatFactory {

    @Value("${abc.com.key-store}")
    String abcComKeyStore;

    @Value("${xyz.com.key-store}")
    String xyzComKeyStore;

    @Value("${server.port}")
    int serverPort;

    @Value("${server.http.port:8080}")
    private int httpPort;

    @Bean
    public ServletWebServerFactory servletContainer() throws Exception {
        TomcatServletWebServerFactory factor = new TomcatServletWebServerFactory();
        factor.addAdditionalTomcatConnectors(createSSLConnectorForMultipleHosts());
        return factor;
    }

    private Connector createSSLConnectorForMultipleHosts() {
        Connector connector = null;
        try {
            connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
            connector.setScheme("https");
            connector.setSecure(true);
            connector.setProperty("SSLEnabled", "true");
            connector.setProperty("defaultSSLHostConfigName", /*"*.abc.com"*/ "*.hostname.com");
            connector.setPort(8444);

            // *.abc.com
            SSLHostConfig sslHostConfig = new SSLHostConfig();
            sslHostConfig.setHostName("*.abc.com");

            SSLHostConfigCertificate sslHostConfigCertificate = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.EC);
            sslHostConfigCertificate.setCertificateKeystoreFile(abcComKeyStore);

            sslHostConfig.addCertificate(sslHostConfigCertificate);
            connector.addSslHostConfig(sslHostConfig);

            // *.xyz.com
            sslHostConfig = new SSLHostConfig();
            sslHostConfig.setHostName("*.xyz.com");

            sslHostConfigCertificate = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.EC);
            sslHostConfigCertificate.setCertificateKeystoreFile(xyzComKeyStore);

            sslHostConfig.addCertificate(sslHostConfigCertificate);
            connector.addSslHostConfig(sslHostConfig);
        } catch (Exception e) {
            System.out.println("Catched exception!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            e.printStackTrace();
        }
        return connector;
    }
}

This code is based on this topic. But I have an error:

2022-01-13 16:10:21.301 ERROR 22364 --- [           main] org.apache.catalina.util.LifecycleBase   : Failed to initialize component [Connector[HTTP/1.1-8444]]

org.apache.catalina.LifecycleException: Protocol handler initialization failed
...
Caused by: java.lang.IllegalArgumentException: Keystore was tampered with, or password was incorrect
...
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
...
Caused by: java.security.UnrecoverableKeyException: Password verification failed

Looks like I miss somethimg.

So, how can I force embedded Tomcat to enable SNI?

Igor_M
  • 308
  • 2
  • 12
  • Please try to replace `abc.com` and `xyz.com` with `my.host...` *consistently* (and not mixing;) – xerx593 Jan 13 '22 at 15:22
  • @xerx593, I replaced them with `my.hostname.com` and `my.another.hostname.com`. Didn't help. – Igor_M Jan 13 '22 at 15:32
  • And what about defaultSSLHostConfigName? – xerx593 Jan 13 '22 at 15:33
  • 1
    @xerx593, changed to `my.hostname.com` and `my.another.hostname.com` (tried both variants) - doesn't help. The question about this error is: what does spring verify? I didn't configure any password. – Igor_M Jan 13 '22 at 15:39
  • What format did you use for those keystores? Try setting the correct one with `setCertificateKeystoreType`. – Piotr P. Karwasz Jan 13 '22 at 20:11

1 Answers1

1

I solved the problem. Maybe my explanation will help somebody. I just followed the errors that I saw and configured all data that server missed. I don't configure certificate, because I have keystore that stores this certificate. My final server has 2 hosts that support only TLSv1.3 (for my needs):

private Connector createSSLConnectorForMultipleHosts() {
    Connector connector = null;
    try {
        Http2Protocol http2Protocol = new Http2Protocol();
        Http11NioProtocol http11Protocol = new Http11NioProtocol();
        http2Protocol.setHttp11Protocol(http11Protocol);

        connector = new Connector(http11Protocol);
        connector.addUpgradeProtocol(http2Protocol);
        connector.setScheme("https");
        connector.setSecure(true);
        connector.setProperty("SSLEnabled", "true");
        connector.setProperty("defaultSSLHostConfigName", "my.hostname.com");
        connector.setPort(8445);

        // first
        SSLHostConfig sslHostConfig = new SSLHostConfig();
        sslHostConfig.setHostName("my.hostname.com");
        sslHostConfig.setCertificateKeystorePassword("mypassword");
        sslHostConfig.setCertificateKeystoreFile(firstKeyStore);
        sslHostConfig.setProtocols("TLSv1.3");
        connector.addSslHostConfig(sslHostConfig);

        // second
        sslHostConfig = new SSLHostConfig();
        sslHostConfig.setHostName("my.another.hostname.com");
        sslHostConfig.setCertificateKeystorePassword("mypassword");
        sslHostConfig.setCertificateKeystoreFile(secondKeyStore);
        sslHostConfig.setProtocols("TLSv1.3");
        connector.addSslHostConfig(sslHostConfig);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return connector;
}
Igor_M
  • 308
  • 2
  • 12