Setting Up HTTPS For Spring Boot

[UPDATE: See an updated technique for https with spring boot here: https://thoughtfulsoftware.wordpress.com/2015/01/25/setting-up-https-for-spring-boot-2/ We now return you to your regularly scheduled blog post]

Lack of HTTPS support in any of the spring.io guides is a problem, and people are asking questions. Fortunately, there is an answer.

There is a howto for many common tasks in spring boot, including adding support for HTTPS. The main idea is that a TomcatConnectorCustomizer in a Spring Boot application will give you a callback reference to the Connector which you can then use to customize the Tomcat connectors. With that you can apply SSL to the embedded Tomcat instance.

Let’s get started. First of all, we need a keystore. Note the -alias flag which we will use for configuration later, and the -validity flag which here makes a key valid for 10 years (3650 days).

> keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
Enter keystore password:  
Re-enter new password:
What is your first and last name?
  [Unknown]:  
What is the name of your organizational unit?
  [Unknown]:  
What is the name of your organization?
  [Unknown]:  
What is the name of your City or Locality?
  [Unknown]:  
What is the name of your State or Province?
  [Unknown]:  
What is the two-letter country code for this unit?
  [Unknown]:  
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
  [no]:  yes

The contents of the keystore can then be inspected. You can inspect the store to verify the keystore type, keystore provider, and review the alias name for use in configuring your code. (Note that to view a PKCS12 keystore, you need to specify the storetype)

> keytool -list -v -keystore keystore.p12 -storetype pkcs12
Enter keystore password:  

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: tomcat
...

With a keystore file in place and all the necessary configuration information, we can configure our server.

The first approach shown here is to create an EmbeddedServletContainerFactory and provide it as a @Bean. According to the docs, this is a heavier solution than customizing the one provided automatically, but the code for either approach is essentially the same. At the end of the day we just need a container factor and we need to add a TomcatConnectorCustomizer.

For a little extra flair, we’re also using Spring 4’s support for Java 8 lambdas, meaning we can use lambda expressions instead of anonymous inner classes.

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        
        // keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
        // keytool -list -v -keystore keystore.p12 -storetype pkcs12
        
        // curl -u user:password -k https://127.0.0.1:9000/greeting
        
        final String keystoreFile = "/absolute/path/to/keystore.p12";
        final String keystorePass = "mypassword";
        final String keystoreType = "PKCS12";
        final String keystoreProvider = "SunJSSE";
        final String keystoreAlias = "tomcat";

        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addConnectorCustomizers((TomcatConnectorCustomizer) (Connector con) -> {
            con.setScheme("https");
            con.setSecure(true);
            Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();
            proto.setSSLEnabled(true);
            proto.setKeystoreFile(keystoreFile);
            proto.setKeystorePass(keystorePass);
            proto.setKeystoreType(keystoreType);
            proto.setProperty("keystoreProvider", keystoreProvider);
            proto.setKeyAlias(keystoreAlias);
        });

        
        return factory;
    }

The second approach shown here is what Spring recommends in the howto guide. The difference here is that we are customizing the servlet container that the framework provides, instead of creating the container ourselves and customizing it. Also in this sample we are using injected properties that were defined in the application.properties file. Finally, this code does not require us to specify the keystore provider.

    @Bean
    @Inject
    public EmbeddedServletContainerCustomizer containerCustomizer(@Value("${keystore.file}") String keystoreFile,
                                                                  @Value("${keystore.password}") String keystorePassword,
                                                                  @Value("${keystore.type}") String keystoreType,
                                                                  @Value("${keystore.alias}") String keystoreAlias) throws FileNotFoundException
    {
        final String absoluteKeystoreFile = ResourceUtils.getFile(keystoreFile).getAbsolutePath();
        
        return (ConfigurableEmbeddedServletContainerFactory factory) -> {
            TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) factory;
            containerFactory.addConnectorCustomizers((TomcatConnectorCustomizer) (Connector connector) -> {
                connector.setSecure(true);
                connector.setScheme("https");
                connector.setAttribute("keystoreFile", absoluteKeystoreFile);
                connector.setAttribute("keystorePass", keystorePassword);
                connector.setAttribute("keystoreType", keystoreType);
                connector.setAttribute("keyAlias", keystoreAlias);
                connector.setAttribute("clientAuth", "false");
                connector.setAttribute("sslProtocol", "TLS");
                connector.setAttribute("SSLEnabled", true);
            });
        };
    }

And Voila! We can now run our application on HTTPS.

Advertisements

6 Comments

Filed under Software Engineering

6 responses to “Setting Up HTTPS For Spring Boot

  1. Christian

    Thanks a bunch from a Norwegian dev.

  2. Appasamy

    How do I allow only https and not http

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s