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 c60cb4d983f..16b53d44a00 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 @@ -44,6 +44,7 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; /** * {@link JettyServerCustomizer} that configures SSL on the given Jetty server instance. @@ -224,10 +225,8 @@ class SslServerCustomizer implements JettyServerCustomizer { String keystoreType = (ssl.getKeyStoreType() != null) ? ssl.getKeyStoreType() : "JKS"; String keystoreLocation = ssl.getKeyStore(); if (keystoreType.equalsIgnoreCase("PKCS11")) { - if (keystoreLocation != null && !keystoreLocation.isEmpty()) { - throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '" - + keystoreLocation + "'. Must be undefined / null."); - } + Assert.state(!StringUtils.hasText(keystoreLocation), + () -> "Keystore location '" + keystoreLocation + "' must be empty or null for PKCS11 key stores"); } else { try { @@ -240,7 +239,7 @@ class SslServerCustomizer implements JettyServerCustomizer { } factory.setKeyStoreType(keystoreType); if (ssl.getKeyStoreProvider() != null) { - factory.setKeyStoreProvider(ssl.getKeyStoreProvider()); + factory.setKeyStoreProvider(this.ssl.getKeyStoreProvider()); } } 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 d00ff005ed6..5fe36066db7 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 @@ -48,7 +48,9 @@ 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; import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; /** * {@link NettyServerCustomizer} that configures SSL for the given Reactor Netty server @@ -169,25 +171,26 @@ public class SslServerCustomizer implements NettyServerCustomizer { return loadStore(type, provider, resource, password); } - private KeyStore loadStore(String type, String provider, String resource, String password) throws Exception { - type = (type != null) ? type : "JKS"; - KeyStore store = (provider != null) ? KeyStore.getInstance(type, provider) : KeyStore.getInstance(type); - if (type.equalsIgnoreCase("PKCS11")) { - if (resource != null && !resource.isEmpty()) { - throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '" - + resource + "'. Must be undefined / null."); - } - store.load(null, (password != null) ? password.toCharArray() : null); + private KeyStore loadStore(String keystoreType, String provider, String keystoreLocation, String password) + throws Exception { + keystoreType = (keystoreType != null) ? keystoreType : "JKS"; + char[] passwordChars = (password != null) ? password.toCharArray() : null; + KeyStore store = (provider != null) ? KeyStore.getInstance(keystoreType, provider) + : KeyStore.getInstance(keystoreType); + if (keystoreType.equalsIgnoreCase("PKCS11")) { + Assert.state(!StringUtils.hasText(keystoreLocation), + () -> "Keystore location '" + keystoreLocation + "' must be empty or null for PKCS11 key stores"); + store.load(null, passwordChars); } else { try { - URL url = ResourceUtils.getURL(resource); + URL url = ResourceUtils.getURL(keystoreLocation); try (InputStream stream = url.openStream()) { - store.load(stream, (password != null) ? password.toCharArray() : null); + store.load(stream, passwordChars); } } catch (Exception ex) { - throw new WebServerException("Could not load key store '" + resource + "'", ex); + throw new WebServerException("Could not load key store '" + keystoreLocation + "'", ex); } } return store; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java index c243d88ef4b..673d7c69116 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java @@ -143,10 +143,8 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { String keystoreType = (ssl.getKeyStoreType() != null) ? ssl.getKeyStoreType() : "JKS"; String keystoreLocation = ssl.getKeyStore(); if (keystoreType.equalsIgnoreCase("PKCS11")) { - if (keystoreLocation != null && !keystoreLocation.isEmpty()) { - throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '" - + keystoreLocation + "'. Must be undefined / null."); - } + Assert.state(!StringUtils.hasText(keystoreLocation), + () -> "Keystore location '" + keystoreLocation + "' must be empty or null for PKCS11 key stores"); } else { try { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizerTests.java index c63d3ba1fb7..4122e0e5b2b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizerTests.java @@ -17,8 +17,6 @@ package org.springframework.boot.web.embedded.jetty; import java.net.InetSocketAddress; -import java.security.Provider; -import java.security.Security; import java.util.ArrayList; import java.util.List; @@ -29,20 +27,19 @@ import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.OS; import org.springframework.boot.testsupport.junit.DisabledOnOs; -import org.springframework.boot.web.embedded.netty.MockPkcs11SecurityProvider; +import org.springframework.boot.web.embedded.test.MockPkcs11Security; +import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider; import org.springframework.boot.web.server.Http2; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServerException; 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.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatNoException; /** @@ -51,25 +48,9 @@ import static org.assertj.core.api.Assertions.assertThatNoException; * @author Andy Wilkinson * @author Cyril Dangerville */ +@MockPkcs11Security class SslServerCustomizerTests { - private static final Provider PKCS11_PROVIDER = new MockPkcs11SecurityProvider(); - - @BeforeAll - static void beforeAllTests() { - /* - * Add the mock Java security provider for PKCS#11-related unit tests. - * - */ - Security.addProvider(PKCS11_PROVIDER); - } - - @AfterAll - static void afterAllTests() { - // Remove the provider previously added in setup() - Security.removeProvider(PKCS11_PROVIDER.getName()); - } - @Test @SuppressWarnings("rawtypes") void whenHttp2IsNotEnabledServerConnectorHasSslAndHttpConnectionFactories() { @@ -107,11 +88,8 @@ class SslServerCustomizerTests { assertThat(((ALPNServerConnectionFactory) factories.get(1)).getDefaultProtocol()).isNull(); } - /** - * Null/undefined keystore is invalid unless keystore type is PKCS11. - */ @Test - void configureSslWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() { + void configureSslWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() { Ssl ssl = new Ssl(); SslServerCustomizer customizer = new SslServerCustomizer(null, ssl, null, null); assertThatExceptionOfType(Exception.class) @@ -122,30 +100,26 @@ class SslServerCustomizerTests { }); } - /** - * No keystore path should be defined if keystore type is PKCS#11. - */ @Test - void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsIllegalArgumentException() { + void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyPassword("password"); SslServerCustomizer customizer = new SslServerCustomizer(null, ssl, null, null); - assertThatIllegalArgumentException() + assertThatIllegalStateException() .isThrownBy(() -> customizer.configureSsl(new SslContextFactory.Server(), ssl, null)) - .withMessageContaining("Input keystore location is not valid for keystore type 'PKCS11'"); + .withMessageContaining("must be empty or null for PKCS11 key stores"); } @Test void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStorePassword("1234"); SslServerCustomizer customizer = new SslServerCustomizer(null, ssl, null, null); - // Loading the KeyManagerFactory should be successful assertThatNoException().isThrownBy(() -> customizer.configureSsl(new SslContextFactory.Server(), ssl, null)); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockPkcs11SecurityProvider.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockPkcs11SecurityProvider.java deleted file mode 100644 index 5bc49afbd73..00000000000 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockPkcs11SecurityProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2022 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.embedded.netty; - -import java.security.KeyStoreSpi; -import java.security.Provider; - -/** - * Mock PKCS#11 Security Provider for testing purposes only (e.g. SslServerCustomizerTests - * class) - * - * @author Cyril Dangerville - */ -public class MockPkcs11SecurityProvider extends Provider { - - private static final String DEFAULT_PROVIDER_NAME = "Mock-PKCS11"; - - private static final double VERSION = 0.1; - - private static final String DESCRIPTION = "Mock PKCS11 Provider"; - - /** - * Create Security Provider named {@value #DEFAULT_PROVIDER_NAME}, version - * {@value #VERSION} and providing PKCS11 KeyStores with {@link MockKeyStoreSpi} as - * {@link KeyStoreSpi} implementation. - */ - public MockPkcs11SecurityProvider() { - super(DEFAULT_PROVIDER_NAME, VERSION, DESCRIPTION); - - putService(new Service(this, "KeyStore", "PKCS11", - "org.springframework.boot.web.embedded.netty.MockKeyStoreSpi", null, null)); - } - -} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/SslServerCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/SslServerCustomizerTests.java index 1c2a17612ad..4b84b2b2f14 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/SslServerCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/SslServerCustomizerTests.java @@ -17,13 +17,11 @@ package org.springframework.boot.web.embedded.netty; import java.security.NoSuchProviderException; -import java.security.Provider; -import java.security.Security; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.springframework.boot.web.embedded.test.MockPkcs11Security; +import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServerException; @@ -38,29 +36,9 @@ import static org.assertj.core.api.Assertions.assertThatNoException; * @author Cyril Dangerville */ @SuppressWarnings("deprecation") +@MockPkcs11Security class SslServerCustomizerTests { - private static final Provider PKCS11_PROVIDER = new MockPkcs11SecurityProvider(); - - @BeforeAll - static void setup() { - /* - * Add the mock Java security provider for PKCS#11-related unit tests. - * - * For an integration test with an actual PKCS#11 library - SoftHSM - properly - * installed and configured on the system (inside a container), used via Java - * built-in SunPKCS11 provider, see the 'spring-boot-smoke-test-webflux-ssl' - * project in 'spring-boot-tests/spring-boot-smoke-tests' folder. - */ - Security.addProvider(PKCS11_PROVIDER); - } - - @AfterAll - static void shutdown() { - // Remove the provider previously added in setup() - Security.removeProvider(PKCS11_PROVIDER.getName()); - } - @Test void keyStoreProviderIsUsedWhenCreatingKeyStore() { Ssl ssl = new Ssl(); @@ -85,41 +63,34 @@ class SslServerCustomizerTests { .withMessageContaining("com.example.TrustStoreProvider"); } - /** - * Null/undefined keystore is not valid unless keystore type is PKCS11. - */ @Test - void getKeyManagerFactoryWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() { + void getKeyManagerFactoryWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() { Ssl ssl = new Ssl(); SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null); assertThatIllegalStateException().isThrownBy(() -> customizer.getKeyManagerFactory(ssl, null)) .withCauseInstanceOf(WebServerException.class).withMessageContaining("Could not load key store 'null'"); } - /** - * No keystore path should be defined if keystore type is PKCS#11. - */ @Test - void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsIllegalArgumentException() { + void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyPassword("password"); SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null); assertThatIllegalStateException().isThrownBy(() -> customizer.getKeyManagerFactory(ssl, null)) - .withCauseInstanceOf(IllegalArgumentException.class) - .withMessageContaining("Input keystore location is not valid for keystore type 'PKCS11'"); + .withCauseInstanceOf(IllegalStateException.class) + .withMessageContaining("must be empty or null for PKCS11 key stores"); } @Test void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStorePassword("1234"); SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null); - // Loading the KeyManagerFactory should be successful assertThatNoException().isThrownBy(() -> customizer.getKeyManagerFactory(ssl, null)); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockKeyStoreSpi.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockKeyStoreSpi.java similarity index 89% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockKeyStoreSpi.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockKeyStoreSpi.java index 9f9aee61ca7..d6452a644c0 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/MockKeyStoreSpi.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockKeyStoreSpi.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.web.embedded.netty; +package org.springframework.boot.web.embedded.test; import java.io.InputStream; import java.io.OutputStream; @@ -31,7 +31,7 @@ import java.util.HashMap; import java.util.Map; /** - * Mock Security Provider for testing purposes only (e.g. SslServerCustomizerTests class) + * Mock Security Provider for testing purposes. * * @author Cyril Dangerville */ @@ -45,7 +45,7 @@ public class MockKeyStoreSpi extends KeyStoreSpi { KEYGEN.initialize(2048); } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); + throw new IllegalStateException(ex); } } @@ -99,8 +99,6 @@ public class MockKeyStoreSpi extends KeyStoreSpi { @Override public boolean engineContainsAlias(String alias) { - // contains any required alias, for testing purposes - // Add alias to aliases list on the fly this.aliases.put(alias, KEYGEN.generateKeyPair()); return true; } @@ -112,7 +110,6 @@ public class MockKeyStoreSpi extends KeyStoreSpi { @Override public boolean engineIsKeyEntry(String alias) { - // Handle all keystore entries as key entries return this.aliases.containsKey(alias); } @@ -133,7 +130,6 @@ public class MockKeyStoreSpi extends KeyStoreSpi { @Override public void engineLoad(InputStream stream, char[] password) { - // Nothing to do, this is a mock keystore implementation, for testing only. } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11Security.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11Security.java new file mode 100644 index 00000000000..9e726af10e2 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11Security.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-2022 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.embedded.test; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * JUnit {@link ExtendWith @ExtendWith} annotation to support + * {@link MockPkcs11SecurityProvider}. + * + * @author Phillip Webb + */ +@ExtendWith(MockPkcs11SecurityProviderExtension.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface MockPkcs11Security { + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProvider.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProvider.java new file mode 100644 index 00000000000..d6cec239ca9 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2022 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.embedded.test; + +import java.security.Provider; + +/** + * Mock PKCS#11 Security Provider for testing purposes. + * + * @author Cyril Dangerville + */ +public class MockPkcs11SecurityProvider extends Provider { + + /** + * The name of the mock provider. + */ + public static final String NAME = "Mock-PKCS11"; + + static final MockPkcs11SecurityProvider INSTANCE = new MockPkcs11SecurityProvider(); + + MockPkcs11SecurityProvider() { + super(NAME, 0.1, "Mock PKCS11 Provider"); + putService(new Service(this, "KeyStore", "PKCS11", MockKeyStoreSpi.class.getName(), null, null)); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProviderExtension.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProviderExtension.java new file mode 100644 index 00000000000..627e7b50752 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/test/MockPkcs11SecurityProviderExtension.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2022 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.embedded.test; + +import java.security.Security; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * {@link Extension} to support {@link MockPkcs11SecurityProvider}. + * + * @author Phillip Webb + * @see MockPkcs11Security + */ +class MockPkcs11SecurityProviderExtension implements BeforeAllCallback, AfterAllCallback { + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + Security.addProvider(MockPkcs11SecurityProvider.INSTANCE); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + Security.removeProvider(MockPkcs11SecurityProvider.NAME); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java index 3ab6d5a8436..6634d1c1644 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizerTests.java @@ -21,8 +21,6 @@ import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.Security; import java.security.cert.CertificateException; import java.util.Set; @@ -31,9 +29,7 @@ import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfigCertificate; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -41,7 +37,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.web.embedded.netty.MockPkcs11SecurityProvider; +import org.springframework.boot.web.embedded.test.MockPkcs11Security; +import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerException; @@ -50,7 +47,7 @@ import org.springframework.core.io.Resource; 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.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -65,29 +62,13 @@ import static org.mockito.Mockito.mock; */ @ExtendWith(OutputCaptureExtension.class) @DirtiesUrlFactories +@MockPkcs11Security class SslConnectorCustomizerTests { - private static final Provider PKCS11_PROVIDER = new MockPkcs11SecurityProvider(); - private Tomcat tomcat; private Connector connector; - @BeforeAll - static void beforeAllTests() { - /* - * Add the mock Java security provider for PKCS#11-related unit tests. - * - */ - Security.addProvider(PKCS11_PROVIDER); - } - - @AfterAll - static void afterAllTests() { - // Remove the provider previously added in setup() - Security.removeProvider(PKCS11_PROVIDER.getName()); - } - @BeforeEach void setup() { this.tomcat = new Tomcat(); @@ -201,39 +182,32 @@ class SslConnectorCustomizerTests { assertThat(output).doesNotContain("Password verification failed"); } - /** - * Null/undefined keystore is invalid unless keystore type is PKCS11. - */ @Test - void customizeWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() { + void customizeWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() { assertThatExceptionOfType(WebServerException.class) .isThrownBy(() -> new SslConnectorCustomizer(new Ssl(), null).customize(this.tomcat.getConnector())) .withMessageContaining("Could not load key store 'null'"); } - /** - * No keystore path should be defined if keystore type is PKCS#11. - */ @Test - void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsIllegalArgumentException() { + void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyPassword("password"); SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null); - assertThatIllegalArgumentException().isThrownBy(() -> customizer.customize(this.tomcat.getConnector())) - .withMessageContaining("Input keystore location is not valid for keystore type 'PKCS11'"); + assertThatIllegalStateException().isThrownBy(() -> customizer.customize(this.tomcat.getConnector())) + .withMessageContaining("must be empty or null for PKCS11 key stores"); } @Test void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStorePassword("1234"); SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null); - // Loading the KeyManagerFactory should be successful assertThatNoException().isThrownBy(() -> customizer.customize(this.tomcat.getConnector())); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizerTests.java index 1be04a38429..141ac38256f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/SslBuilderCustomizerTests.java @@ -18,16 +18,13 @@ package org.springframework.boot.web.embedded.undertow; import java.net.InetAddress; import java.security.NoSuchProviderException; -import java.security.Provider; -import java.security.Security; import javax.net.ssl.KeyManager; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.springframework.boot.web.embedded.netty.MockPkcs11SecurityProvider; +import org.springframework.boot.web.embedded.test.MockPkcs11Security; +import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServerException; import org.springframework.test.util.ReflectionTestUtils; @@ -43,25 +40,9 @@ import static org.assertj.core.api.Assertions.assertThatNoException; * @author Raheela Aslam * @author Cyril Dangerville */ +@MockPkcs11Security class SslBuilderCustomizerTests { - private static final Provider PKCS11_PROVIDER = new MockPkcs11SecurityProvider(); - - @BeforeAll - static void beforeAllTests() { - /* - * Add the mock Java security provider for PKCS#11-related unit tests. - * - */ - Security.addProvider(PKCS11_PROVIDER); - } - - @AfterAll - static void afterAllTests() { - // Remove the provider previously added in setup() - Security.removeProvider(PKCS11_PROVIDER.getName()); - } - @Test void getKeyManagersWhenAliasIsNullShouldNotDecorate() throws Exception { Ssl ssl = new Ssl(); @@ -100,11 +81,8 @@ class SslBuilderCustomizerTests { .withMessageContaining("com.example.TrustStoreProvider"); } - /** - * Null/undefined keystore is invalid unless keystore type is PKCS11. - */ @Test - void getKeyManagersWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() throws Exception { + void getKeyManagersWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() throws Exception { Ssl ssl = new Ssl(); SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null); assertThatIllegalStateException() @@ -112,14 +90,11 @@ class SslBuilderCustomizerTests { .withCauseInstanceOf(WebServerException.class).withMessageContaining("Could not load key store 'null'"); } - /** - * No keystore path should be defined if keystore type is PKCS#11. - */ @Test - void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsIllegalArgumentException() throws Exception { + void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() throws Exception { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyPassword("password"); SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null); @@ -133,10 +108,9 @@ class SslBuilderCustomizerTests { void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() throws Exception { Ssl ssl = new Ssl(); ssl.setKeyStoreType("PKCS11"); - ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName()); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); ssl.setKeyStorePassword("1234"); SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null); - // Loading the KeyManagerFactory should be successful assertThatNoException() .isThrownBy(() -> ReflectionTestUtils.invokeMethod(customizer, "getKeyManagers", ssl, null)); }