Mr Hoelee
Mr Hoelee

Reputation: 466

How to set HTTPS SSL Cipher Suite Preference in Spring boot embedded tomcat

I'm trying to set HTTPS SSL cipher suite preference according to server preference rather than auto select based on client & server supported common cipher suite with highest strength.

I'd like to let the server choose for common between server & client having "TLS_ECDHE..." in order to support Forward Secrecy. I've tested in "www.ssllabs.com", client browser will prefer cipher having "TLS_RSA..." rather than "TLS_ECDHE"...

I've noticed java 8 support set cipher suite preference: http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#cipher_suite_preference

I assume spring boot embedded Tomcat will call Java 8 function to choose cipher

Here is what I've done in spring boot application.properties file to set server support ciphers set:

server.ssl.ciphers=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_SHA256,TLS_ECDHE_RSA_WITH_AES_128_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_SHA,TLS_ECDHE_RSA_WITH_AES_256_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_SHA384,TLS_ECDHE_RSA_WITH_AES_256_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_SHA,TLS_DHE_RSA_WITH_AES_128_SHA256,TLS_DHE_RSA_WITH_AES_128_SHA,TLS_DHE_DSS_WITH_AES_128_SHA256,TLS_DHE_RSA_WITH_AES_256_SHA256,TLS_DHE_DSS_WITH_AES_256_SHA,TLS_DHE_RSA_WITH_AES_256_SHA

Hopefully someone can guide me how to override default choose cipher behaviour.

Upvotes: 8

Views: 21386

Answers (3)

Michiel Haisma
Michiel Haisma

Reputation: 874

For Spring Boot 3.0.x with Tomcat 10.1.x this could be a solution. It's an adapted version of the original solution posted by @andy-wilkinson.

import java.util.Arrays;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CipherOrderConfig {

  @Bean
  public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainerCustomizer() {
    return factory ->
      factory.addConnectorCustomizers(connector -> {
        ProtocolHandler protocolHandler = connector.getProtocolHandler();
        if (protocolHandler instanceof AbstractHttp11Protocol<?> httpHandler) {
          Arrays
            .stream(httpHandler.findSslHostConfigs())
            .forEach(sslHostConfig -> sslHostConfig.setHonorCipherOrder(true));
        }
      });
  }
}

It should set the cipher order honor flag to true for all configured SSL host configurations.

Upvotes: 1

MoonLord
MoonLord

Reputation: 61

Here is my solution in Spring Boot 2.3.4.RELEASE and JDK 1.8.
It works fine for me.

import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpsConfiguration {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainerCustomizer() {
        return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
            @Override
            public void customize(TomcatServletWebServerFactory factory) {
                factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                    @Override
                    public void customize(Connector connector) {
                        AbstractHttp11Protocol<?> httpHandler = ((AbstractHttp11Protocol<?>) connector.getProtocolHandler());
                        httpHandler.setUseServerCipherSuitesOrder(true);
                        httpHandler.setSSLProtocol("TLSv1.2");
                        httpHandler.setSSLHonorCipherOrder(true);
                        httpHandler.setCiphers("TLS_EMPTY_RENEGOTIATION_INFO_SCSV,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384");
                    }
                });
            }
        };
    }

}

Upvotes: 2

Andy Wilkinson
Andy Wilkinson

Reputation: 116171

You need to tell the connector's underlying protocol handler to use the server's cipher suite order. You can do so with a WebServerFactoryCustomizer :

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainerCustomizer() {
    return (factory) -> {
        factory.addConnectorCustomizers((c) -> 
            ((AbstractHttp11Protocol<?>) c.getProtocolHandler()).setUseServerCipherSuitesOrder(true));
    };
}

Upvotes: 9

Related Questions