How can I specify my .keystore file with Spring Boot and Tomcat? How can I specify my .keystore file with Spring Boot and Tomcat? spring spring

How can I specify my .keystore file with Spring Boot and Tomcat?


Starting with Spring Boot 1.2, you can configure SSL using application.properties or application.yml. Here's an example for application.properties:

server.port = 8443server.ssl.key-store = classpath:keystore.jksserver.ssl.key-store-password = secretserver.ssl.key-password = another-secret

Same thing with application.yml:

server:  port: 8443  ssl:    key-store: classpath:keystore.jks    key-store-password: secret    key-password: another-secret

Here's a link to the current reference documentation.


For external keystores, prefix with "file:"

server.ssl.key-store=file:config/keystore 


It turns out that there is a way to do this, although I'm not sure I've found the 'proper' way since this required hours of reading source code from multiple projects. In other words, this might be a lot of dumb work (but it works).

First, there is no way to get at the server.xml in the embedded Tomcat, either to augment it or replace it. This must be done programmatically.

Second, the 'require_https' setting doesn't help since you can't set cert info that way. It does set up forwarding from http to https, but it doesn't give you a way to make https work so the forwarding isnt helpful. However, use it with the stuff below, which does make https work.

To begin, you need to provide an EmbeddedServletContainerFactory as explained in the Embedded Servlet Container Support docs. The docs are for Java but the Groovy would look pretty much the same. Note that I haven't been able to get it to recognize the @Value annotation used in their example but its not needed. For groovy, simply put this in a new .groovy file and include that file on the command line when you launch spring boot.

Now, the instructions say that you can customize the TomcatEmbeddedServletContainerFactory class that you created in that code so that you can alter web.xml behavior, and this is true, but for our purposes its important to know that you can also use it to tailor server.xml behavior. Indeed, reading the source for the class and comparing it with the Embedded Tomcat docs, you see that this is the only place to do that. The interesting function is TomcatEmbeddedServletContainerFactory.addConnectorCustomizers(), which may not look like much from the Javadocs but actually gives you the Embedded Tomcat object to customize yourself. Simply pass your own implementation of TomcatConnectorCustomizer and set the things you want on the given Connector in the void customize(Connector con) function. Now, there are about a billion things you can do with the Connector and I couldn't find useful docs for it but the createConnector() function in this this guys personal Spring-embedded-Tomcat project is a very practical guide. My implementation ended up looking like this:

package com.deepdownstudios.serverimport org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizerimport org.springframework.boot.context.embedded.EmbeddedServletContainerFactoryimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactoryimport org.apache.catalina.connector.Connector;import org.apache.coyote.http11.Http11NioProtocol;import org.springframework.boot.*import org.springframework.stereotype.*@Configurationclass MyConfiguration {@Beanpublic EmbeddedServletContainerFactory servletContainer() {final int port = 8443;final String keystoreFile = "/path/to/keystore"final String keystorePass = "keystore-password"final String keystoreType = "pkcs12"final String keystoreProvider = "SunJSSE"final String keystoreAlias = "tomcat"TomcatEmbeddedServletContainerFactory factory =         new TomcatEmbeddedServletContainerFactory(this.port);factory.addConnectorCustomizers( new TomcatConnectorCustomizer() {    void    customize(Connector con) {        Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();            proto.setSSLEnabled(true);        con.setScheme("https");        con.setSecure(true);        proto.setKeystoreFile(keystoreFile);        proto.setKeystorePass(keystorePass);        proto.setKeystoreType(keystoreType);        proto.setProperty("keystoreProvider", keystoreProvider);        proto.setKeyAlias(keystoreAlias);    }});return factory;}}

The Autowiring will pick up this implementation an run with it. Once I fixed my busted keystore file (make sure you call keytool with -storetype pkcs12, not -storepass pkcs12 as reported elsewhere), this worked. Also, it would be far better to provide the parameters (port, password, etc) as configuration settings for testing and such... I'm sure its possible if you can get the @Value annotation to work with Groovy.