This ticket has been created out of a discussion originally raised here: https://github.com/spring-projects/spring-boot/issues/13590 Overview: I am trying to configure ssl using a dks-keystore through 'application.properties'. I raised a ticket with the team handling spring-boot and they have stated that there is currently no mechanism for them to be able to stop tomcat from calling 'java.security.KeyStore.load(InputStream, char[])' in favor of 'java.security.KeyStore.load(URI, DomainLoadStoreParameter)' Here is the stacktrace I provided, showing the path that is taken for the configuration of the SSL context: java.lang.UnsupportedOperationException: This keystore must be loaded using a DomainLoadStoreParameter at sun.security.provider.DomainKeyStore.engineLoad(DomainKeyStore.java:713) ~[na:1.8.0_111] at sun.security.provider.DomainKeyStore$DKS.engineLoad(DomainKeyStore.java:68) ~[na:1.8.0_111] at java.security.KeyStore.load(KeyStore.java:1445) ~[na:1.8.0_111] at org.apache.tomcat.util.net.SSLUtilBase.getStore(SSLUtilBase.java:136) ~[tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.SSLHostConfigCertificate.getCertificateKeystore(SSLHostConfigCertificate.java:187) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.jsse.JSSEUtil.getKeyManagers(JSSEUtil.java:185) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:112) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:85) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:225) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:978) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:628) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.catalina.connector.Connector.startInternal(Connector.java:993) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.apache.catalina.core.StandardService.addConnector(StandardService.java:225) [tomcat-embed-core-8.5.14.jar!/:8.5.14] at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.addPreviouslyRemovedConnectors(TomcatEmbeddedServletContainer.java:247) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:190) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:297) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:145) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545) [spring-context-4.3.8.RELEASE.jar!/:4.3.8.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE] at [Redacted] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111] at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) [giant2-ccp-example.jar:1.19.20-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) [giant2-ccp-example.jar:1.19.20-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) [giant2-ccp-example.jar:1.19.20-SNAPSHOT] at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) [giant2-ccp-example.jar:1.19.20-SNAPSHOT]
This looks like it should be fairly easy to implement along the same lines as Spring Boot - i.e. store.load(new DomainLoadStoreParameter(url.toURI(), Collections.emptyMap())); There is a small additional complication that this is a Java 8 feature and Tomcat 8.x has to run on Java 7. We'll need to go via Tomcat's JreCompat. It would be good if you are able to test any implementation before it gets included in a release. Are you able to build Tomcat trunk (9.0.x) from source and test it?
Cool. Thanks, Mark. I am willing and able to build from trunk (or a branch) or use a snapshot when something's available,
Great. Thanks Andy. I've added a first pass at DKS support to trunk (9.0.x). You should be able to specify "DKS" for ConfigFileLoader.getURI(path) and the URI for certificateKeystoreFile. Once this works (hopefully it will work first time) I'll add some docs and back-port it to 8.5.x.
I've built trunk and the DKS keystore is now being handled specially but it doesn't appear to be working correctly: org.apache.catalina.LifecycleException: Protocol handler start failed at org.apache.catalina.connector.Connector.startInternal(Connector.java:960) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.catalina.core.StandardService.addConnector(StandardService.java:225) [tomcat-embed-core.jar:9.0.11-dev] at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.addPreviouslyRemovedConnectors(TomcatWebServer.java:256) [classes/:na] at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:198) [classes/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.startWebServer(ServletWebServerApplicationContext.java:300) [classes/:na] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:162) [classes/:na] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) [spring-context-5.1.0.BUILD-SNAPSHOT.jar:5.1.0.BUILD-SNAPSHOT] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) [classes/:na] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:769) [classes/:na] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) [classes/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) [classes/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1252) [classes/:na] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1240) [classes/:na] at sample.tomcat.ssl.SampleTomcatSslApplication.main(SampleTomcatSslApplication.java:26) [classes/:na] Caused by: java.lang.IllegalArgumentException: Error setting key entry for 'app1 spring-boot-ssl-sample' at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:114) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:85) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:224) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:1107) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:550) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.catalina.connector.Connector.startInternal(Connector.java:957) ~[tomcat-embed-core.jar:9.0.11-dev] ... 14 common frames omitted Caused by: java.security.KeyStoreException: Error setting key entry for 'app1 spring-boot-ssl-sample' at sun.security.provider.DomainKeyStore.engineSetKeyEntry(DomainKeyStore.java:269) ~[na:1.8.0_151] at sun.security.provider.DomainKeyStore$DKS.engineSetKeyEntry(DomainKeyStore.java:68) ~[na:1.8.0_151] at java.security.KeyStore.setKeyEntry(KeyStore.java:1140) ~[na:1.8.0_151] at org.apache.tomcat.util.net.jsse.JSSEUtil.getKeyManagers(JSSEUtil.java:257) ~[tomcat-embed-core.jar:9.0.11-dev] at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:112) ~[tomcat-embed-core.jar:9.0.11-dev] ... 19 common frames omitted This error is occurring because DomainKeyStore$DKS.getKeyStoreForWriting is being called with 'app1 spring-boot-ssl-sample' and returning null. The passed in String is split on space and the first component, app1, is used as the key for a map lookup. The map only contains a single entry with the key iostream1 so it returns null when asked for app1. The iostream1 entry is written as a result of the ksUsed.load(null, null) call on line 256 of JSSEUtil.getKeyManagers(). Prior to the call to load, the map is empty. By contrast to ksUsed, at this point the map within ks contains a single entry named app1. In short, it appears that the switch to an in-memory store for a PKSC#8 key does not work. I'm insufficiently experienced with DKS to know if the above is expected behaviour. If it is expected, the diagnostics could be approved as I could only determine the above by stepping through in the debugger.
I've disabled in-memory keystores for DKS in trunk. If you could test again and let me know how you get on...
Thanks, Mark. It looks good to me now. I've got embedded Tomcat using a JKS KeyStore that's been loaded via an entry in a DKS KeyStore.
Thanks for the testing. I've back-ported it to 8.5.x as well although I had to use reflection as DKS requires Java 8 (and 8.5.x is Java 7 or later). Fixed in: - trunk for 9.0.11 onwards - 8.5.x for 8.5.33 onwards