diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java index 31f3e2f0dd9..666021b657a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http2.HTTP2Cipher; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -36,6 +37,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.springframework.boot.web.server.Http2; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.SslConfigurationValidator; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerException; import org.springframework.util.Assert; @@ -48,6 +50,7 @@ import org.springframework.util.ResourceUtils; * * @author Brian Clozel * @author Olivier Lamy + * @author Chris Bono */ class SslServerCustomizer implements JettyServerCustomizer { @@ -105,7 +108,8 @@ class SslServerCustomizer implements JettyServerCustomizer { HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config); SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); - return new ServerConnector(server, sslConnectionFactory, connectionFactory); + return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory, + connectionFactory); } private boolean isAlpnPresent() { @@ -123,7 +127,8 @@ class SslServerCustomizer implements JettyServerCustomizer { sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR); sslContextFactory.setProvider("Conscrypt"); SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol()); - return new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(config)); + return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), ssl, alpn, h2, + new HttpConnectionFactory(config)); } /** @@ -215,4 +220,35 @@ class SslServerCustomizer implements JettyServerCustomizer { } } + /** + * A {@link ServerConnector} that validates the ssl key alias on server startup. + */ + static class SslValidatingServerConnector extends ServerConnector { + + private SslContextFactory sslContextFactory; + + private String keyAlias; + + SslValidatingServerConnector(Server server, SslContextFactory sslContextFactory, String keyAlias, + SslConnectionFactory sslConnectionFactory, HttpConnectionFactory connectionFactory) { + super(server, sslConnectionFactory, connectionFactory); + this.sslContextFactory = sslContextFactory; + this.keyAlias = keyAlias; + } + + SslValidatingServerConnector(Server server, SslContextFactory sslContextFactory, String keyAlias, + ConnectionFactory... factories) { + super(server, factories); + this.sslContextFactory = sslContextFactory; + this.keyAlias = keyAlias; + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + SslConfigurationValidator.validateKeyAlias(this.sslContextFactory.getKeyStore(), this.keyAlias); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java index f2e32e46325..eeab11a5f92 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ import reactor.netty.tcp.SslProvider; import org.springframework.boot.web.server.Http2; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.SslConfigurationValidator; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerException; import org.springframework.util.ResourceUtils; @@ -106,6 +107,7 @@ public class SslServerCustomizer implements NettyServerCustomizer { protected KeyManagerFactory getKeyManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) { try { KeyStore keyStore = getKeyStore(ssl, sslStoreProvider); + SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias()); KeyManagerFactory keyManagerFactory = (ssl.getKeyAlias() == null) ? KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) : new ConfigurableAliasKeyManagerFactory(ssl.getKeyAlias(), diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizer.java index 95a7e418482..65c948be88b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ import org.xnio.Sequence; import org.xnio.SslClientAuthMode; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.SslConfigurationValidator; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerException; import org.springframework.util.ResourceUtils; @@ -107,6 +108,7 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { private KeyManager[] getKeyManagers(Ssl ssl, SslStoreProvider sslStoreProvider) { try { KeyStore keyStore = getKeyStore(ssl, sslStoreProvider); + SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias()); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); char[] keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword().toCharArray() : null; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/SslConfigurationValidator.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/SslConfigurationValidator.java new file mode 100644 index 00000000000..a04027619a9 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/SslConfigurationValidator.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.server; + +import java.security.KeyStore; +import java.security.KeyStoreException; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Provides utilities around SSL. + * + * @author Chris Bono + * @since 2.1.13 + */ +public final class SslConfigurationValidator { + + private SslConfigurationValidator() { + } + + public static void validateKeyAlias(KeyStore keyStore, String keyAlias) { + if (!StringUtils.isEmpty(keyAlias)) { + try { + Assert.state(keyStore.containsAlias(keyAlias), + () -> String.format("Keystore does not contain specified alias '%s'", keyAlias)); + } + catch (KeyStoreException ex) { + throw new IllegalStateException( + String.format("Could not determine if keystore contains alias '%s'", keyAlias), ex); + } + } + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java index 562a08ece22..7fd98645d5c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java @@ -19,8 +19,6 @@ package org.springframework.boot.web.embedded.netty; import java.time.Duration; import java.util.Arrays; -import javax.net.ssl.SSLHandshakeException; - import org.junit.jupiter.api.Test; import org.mockito.InOrder; import reactor.core.publisher.Mono; @@ -101,14 +99,6 @@ class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor StepVerifier.create(result).expectNext("Hello World").verifyComplete(); } - @Test - void whenSslIsConfiguredWithAnInvalidAliasTheSslHandshakeFails() { - Mono result = testSslWithAlias("test-alias-bad"); - StepVerifier.setDefaultTimeout(Duration.ofSeconds(30)); - StepVerifier.create(result).expectErrorMatches((throwable) -> throwable instanceof SSLHandshakeException - && throwable.getMessage().contains("HANDSHAKE_FAILURE")).verify(); - } - protected Mono testSslWithAlias(String alias) { String keyStore = "classpath:test.jks"; String keyPassword = "password"; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java index 43ad1a9d95a..33ab93d4654 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactoryTests.java @@ -41,12 +41,15 @@ import org.mockito.InOrder; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests; import org.springframework.boot.web.server.PortInUseException; +import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.WebServerException; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -239,6 +242,20 @@ class TomcatReactiveWebServerFactoryTests extends AbstractReactiveWebServerFacto }); } + @Test + void sslWithInvalidAliasFailsDuringStartup() { + String keyStore = "classpath:test.jks"; + String keyPassword = "password"; + AbstractReactiveWebServerFactory factory = getFactory(); + Ssl ssl = new Ssl(); + ssl.setKeyStore(keyStore); + ssl.setKeyPassword(keyPassword); + ssl.setKeyAlias("test-alias-404"); + factory.setSsl(ssl); + assertThatThrownBy(() -> factory.getWebServer(new EchoHandler()).start()) + .isInstanceOf(WebServerException.class); + } + private void doWithBlockedPort(BlockedPortAction action) throws IOException { int port = SocketUtils.findAvailableTcpPort(40000); ServerSocket serverSocket = new ServerSocket(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java index cdb1398ad4b..c35b590c7e2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,8 +64,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.springframework.boot.testsupport.system.CapturedOutput; +import org.springframework.boot.testsupport.web.servlet.ExampleServlet; import org.springframework.boot.web.server.PortInUseException; +import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServerException; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests; import org.springframework.core.io.ByteArrayResource; @@ -82,6 +85,7 @@ import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -543,6 +547,16 @@ class TomcatServletWebServerFactoryTests extends AbstractServletWebServerFactory this.webServer.start(); } + @Test + void sslWithInvalidAliasFailsDuringStartup() { + AbstractServletWebServerFactory factory = getFactory(); + Ssl ssl = getSsl(null, "password", "test-alias-404", "src/test/resources/test.jks"); + factory.setSsl(ssl); + ServletRegistrationBean registration = new ServletRegistrationBean<>( + new ExampleServlet(true, false), "/hello"); + assertThatThrownBy(() -> factory.getWebServer(registration).start()).isInstanceOf(WebServerException.class); + } + @Override protected JspServlet getJspServlet() throws ServletException { Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java index 5efcd52c27b..ec6756af882 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,6 +132,44 @@ public abstract class AbstractReactiveWebServerFactoryTests { assertThat(result.block(Duration.ofSeconds(30))).isEqualTo("Hello World"); } + @Test + void sslWithValidAlias() { + String keyStore = "classpath:test.jks"; + String keyPassword = "password"; + AbstractReactiveWebServerFactory factory = getFactory(); + Ssl ssl = new Ssl(); + ssl.setKeyStore(keyStore); + ssl.setKeyPassword(keyPassword); + ssl.setKeyAlias("test-alias"); + factory.setSsl(ssl); + this.webServer = factory.getWebServer(new EchoHandler()); + this.webServer.start(); + ReactorClientHttpConnector connector = buildTrustAllSslConnector(); + WebClient client = WebClient.builder().baseUrl("https://localhost:" + this.webServer.getPort()) + .clientConnector(connector).build(); + + Mono result = client.post().uri("/test").contentType(MediaType.TEXT_PLAIN) + .body(BodyInserters.fromObject("Hello World")).exchange() + .flatMap((response) -> response.bodyToMono(String.class)); + + StepVerifier.setDefaultTimeout(Duration.ofSeconds(30)); + StepVerifier.create(result).expectNext("Hello World").verifyComplete(); + } + + @Test + void sslWithInvalidAliasFailsDuringStartup() { + String keyStore = "classpath:test.jks"; + String keyPassword = "password"; + AbstractReactiveWebServerFactory factory = getFactory(); + Ssl ssl = new Ssl(); + ssl.setKeyStore(keyStore); + ssl.setKeyPassword(keyPassword); + ssl.setKeyAlias("test-alias-404"); + factory.setSsl(ssl); + assertThatThrownBy(() -> factory.getWebServer(new EchoHandler()).start()) + .hasStackTraceContaining("Keystore does not contain specified alias 'test-alias-404'"); + } + protected ReactorClientHttpConnector buildTrustAllSslConnector() { SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK) .trustManager(InsecureTrustManagerFactory.INSTANCE); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/SslConfigurationValidatorTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/SslConfigurationValidatorTest.java new file mode 100644 index 00000000000..f8c663a4412 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/SslConfigurationValidatorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.server; + +import java.io.File; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests for {@link SslConfigurationValidator}. + * + * @author Chris Bono + */ + +public class SslConfigurationValidatorTest { + + private static final String VALID_ALIAS = "test-alias"; + + private static final String INVALID_ALIAS = "test-alias-5150"; + + private KeyStore keyStore; + + @BeforeEach + void loadKeystore() throws Exception { + this.keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + this.keyStore.load(new FileInputStream(new File("src/test/resources/test.jks")), "secret".toCharArray()); + } + + @Test + void validateKeyAliasWhenAliasFoundShouldNotFail() { + SslConfigurationValidator.validateKeyAlias(this.keyStore, VALID_ALIAS); + } + + @Test + void validateKeyAliasWhenNullAliasShouldNotFail() { + SslConfigurationValidator.validateKeyAlias(this.keyStore, null); + } + + @Test + void validateKeyAliasWhenEmptyAliasShouldNotFail() { + SslConfigurationValidator.validateKeyAlias(this.keyStore, ""); + } + + @Test + void validateKeyAliasWhenAliasNotFoundShouldThrowException() { + assertThatThrownBy(() -> SslConfigurationValidator.validateKeyAlias(this.keyStore, INVALID_ALIAS)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Keystore does not contain specified alias '" + INVALID_ALIAS + "'"); + } + + @Test + void validateKeyAliasWhenKeyStoreThrowsExceptionOnContains() throws KeyStoreException { + KeyStore uninitializedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + assertThatThrownBy(() -> SslConfigurationValidator.validateKeyAlias(uninitializedKeyStore, "alias")) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Could not determine if keystore contains alias 'alias'"); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index aa0ceb0dc6a..66a3da1e5fb 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -126,6 +126,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIOException; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -448,6 +449,17 @@ public abstract class AbstractServletWebServerFactoryTests { assertThat(response).contains("scheme=https"); } + @Test + void sslWithInvalidAliasFailsDuringStartup() { + AbstractServletWebServerFactory factory = getFactory(); + Ssl ssl = getSsl(null, "password", "test-alias-404", "src/test/resources/test.jks"); + factory.setSsl(ssl); + ServletRegistrationBean registration = new ServletRegistrationBean<>( + new ExampleServlet(true, false), "/hello"); + assertThatThrownBy(() -> factory.getWebServer(registration).start()) + .hasStackTraceContaining("Keystore does not contain specified alias 'test-alias-404'"); + } + @Test void serverHeaderIsDisabledByDefaultWhenUsingSsl() throws Exception { AbstractServletWebServerFactory factory = getFactory(); @@ -620,7 +632,7 @@ public abstract class AbstractServletWebServerFactoryTests { return getSsl(clientAuth, keyPassword, keyStore, null, null, null); } - private Ssl getSsl(ClientAuth clientAuth, String keyPassword, String keyAlias, String keyStore) { + protected Ssl getSsl(ClientAuth clientAuth, String keyPassword, String keyAlias, String keyStore) { return getSsl(clientAuth, keyPassword, keyAlias, keyStore, null, null, null); }