Fix bug in webserver start when loading PKCS#11 KeyStore
See gh-32179
This commit is contained in:
committed by
Moritz Halbritter
parent
5b2b122398
commit
716a839d54
@@ -19,6 +19,7 @@ package org.springframework.boot.web.embedded.jetty;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.util.ResourceUtils;
|
||||
* @author Brian Clozel
|
||||
* @author Olivier Lamy
|
||||
* @author Chris Bono
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
class SslServerCustomizer implements JettyServerCustomizer {
|
||||
|
||||
@@ -220,16 +222,25 @@ class SslServerCustomizer implements JettyServerCustomizer {
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(SslContextFactory.Server factory, Ssl ssl) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getKeyStore());
|
||||
factory.setKeyStoreResource(Resource.newResource(url));
|
||||
final String keystoreType = Objects.requireNonNullElse(ssl.getKeyStoreType(), "JKS");
|
||||
final String keystoreLocation = ssl.getKeyStore();
|
||||
if (keystoreType.equalsIgnoreCase("PKCS11")) {
|
||||
if (keystoreLocation != null && !keystoreLocation.isBlank()) {
|
||||
throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '"
|
||||
+ keystoreLocation + "'. Must be undefined / null.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + ssl.getKeyStore() + "'", ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
factory.setKeyStoreType(ssl.getKeyStoreType());
|
||||
else {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(keystoreLocation);
|
||||
factory.setKeyStoreResource(Resource.newResource(url));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + keystoreLocation + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
factory.setKeyStoreType(keystoreType);
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
factory.setKeyStoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.springframework.util.ResourceUtils;
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Chris Bono
|
||||
* @author Cyril Dangerville
|
||||
* @since 2.0.0
|
||||
* @deprecated this class is meant for Spring Boot internal use only.
|
||||
*/
|
||||
@@ -171,17 +172,27 @@ public class SslServerCustomizer implements NettyServerCustomizer {
|
||||
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);
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (InputStream stream = url.openStream()) {
|
||||
store.load(stream, (password != null) ? password.toCharArray() : null);
|
||||
if (type.equalsIgnoreCase("PKCS11")) {
|
||||
if (resource != null && !resource.isBlank()) {
|
||||
throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '"
|
||||
+ resource + "'. Must be undefined / null.");
|
||||
}
|
||||
return store;
|
||||
|
||||
store.load(null, (password != null) ? password.toCharArray() : null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + resource + "'", ex);
|
||||
else {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (InputStream stream = url.openStream()) {
|
||||
store.load(stream, (password != null) ? password.toCharArray() : null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + resource + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.web.embedded.tomcat;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
@@ -39,6 +40,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Brian Clozel
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
class SslConnectorCustomizer implements TomcatConnectorCustomizer {
|
||||
|
||||
@@ -139,15 +141,24 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(SSLHostConfigCertificate certificate, Ssl ssl) {
|
||||
try {
|
||||
certificate.setCertificateKeystoreFile(ResourceUtils.getURL(ssl.getKeyStore()).toString());
|
||||
final String keystoreType = Objects.requireNonNullElse(ssl.getKeyStoreType(), "JKS");
|
||||
final String keystoreLocation = ssl.getKeyStore();
|
||||
if (keystoreType.equalsIgnoreCase("PKCS11")) {
|
||||
if (keystoreLocation != null && !keystoreLocation.isBlank()) {
|
||||
throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '"
|
||||
+ keystoreLocation + "'. Must be undefined / null.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + ssl.getKeyStore() + "'", ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
certificate.setCertificateKeystoreType(ssl.getKeyStoreType());
|
||||
else {
|
||||
try {
|
||||
certificate.setCertificateKeystoreFile(ResourceUtils.getURL(keystoreLocation).toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + keystoreLocation + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
certificate.setCertificateKeystoreType(keystoreType);
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
certificate.setCertificateKeystoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.springframework.util.ResourceUtils;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
class SslBuilderCustomizer implements UndertowBuilderCustomizer {
|
||||
|
||||
@@ -180,16 +181,27 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
|
||||
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);
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (InputStream stream = url.openStream()) {
|
||||
store.load(stream, (password != null) ? password.toCharArray() : null);
|
||||
if (type.equalsIgnoreCase("PKCS11")) {
|
||||
if (resource != null && !resource.isBlank()) {
|
||||
throw new IllegalArgumentException("Input keystore location is not valid for keystore type 'PKCS11': '"
|
||||
+ resource + "'. Must be undefined / null.");
|
||||
}
|
||||
return store;
|
||||
|
||||
store.load(null, (password != null) ? password.toCharArray() : null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + resource + "'", ex);
|
||||
else {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (InputStream stream = url.openStream()) {
|
||||
store.load(stream, (password != null) ? password.toCharArray() : null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new WebServerException("Could not load key store '" + resource + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
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;
|
||||
|
||||
@@ -27,24 +29,47 @@ 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.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.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslServerCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
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() {
|
||||
@@ -82,8 +107,11 @@ class SslServerCustomizerTests {
|
||||
assertThat(((ALPNServerConnectionFactory) factories.get(1)).getDefaultProtocol()).isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Null/undefined keystore is invalid unless keystore type is PKCS11.
|
||||
*/
|
||||
@Test
|
||||
void configureSslWhenSslIsEnabledWithNoKeyStoreThrowsWebServerException() {
|
||||
void configureSslWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() {
|
||||
Ssl ssl = new Ssl();
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(null, ssl, null, null);
|
||||
assertThatExceptionOfType(Exception.class)
|
||||
@@ -94,6 +122,33 @@ class SslServerCustomizerTests {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* No keystore path should be defined if keystore type is PKCS#11.
|
||||
*/
|
||||
@Test
|
||||
void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsIllegalArgumentException() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyPassword("password");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(null, ssl, null, null);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> customizer.configureSsl(new SslContextFactory.Server(), ssl, null))
|
||||
.withMessageContaining("Input keystore location is not valid for keystore type 'PKCS11'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
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));
|
||||
}
|
||||
|
||||
private Server createCustomizedServer() {
|
||||
return createCustomizedServer(new Http2());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStoreSpi;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mock Security Provider for testing purposes only (e.g. SslServerCustomizerTests class)
|
||||
*
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
public class MockKeyStoreSpi extends KeyStoreSpi {
|
||||
|
||||
private static final KeyPairGenerator KEYGEN;
|
||||
|
||||
static {
|
||||
try {
|
||||
KEYGEN = KeyPairGenerator.getInstance("RSA");
|
||||
KEYGEN.initialize(2048);
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, KeyPair> aliases = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Key engineGetKey(String alias, char[] password) {
|
||||
final KeyPair keyPair = this.aliases.get(alias);
|
||||
return (keyPair != null) ? keyPair.getPrivate() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] engineGetCertificateChain(String alias) {
|
||||
return new Certificate[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate engineGetCertificate(String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date engineGetCreationDate(String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineSetCertificateEntry(String alias, Certificate cert) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineDeleteEntry(String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> engineAliases() {
|
||||
return Collections.enumeration(this.aliases.keySet());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int engineSize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean engineIsKeyEntry(String alias) {
|
||||
// Handle all keystore entries as key entries
|
||||
return this.aliases.containsKey(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean engineIsCertificateEntry(String alias) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String engineGetCertificateAlias(Certificate cert) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineStore(OutputStream stream, char[] password) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void engineLoad(InputStream stream, char[] password) {
|
||||
// Nothing to do, this is a mock keystore implementation, for testing only.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 String 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,23 +17,50 @@
|
||||
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.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslServerCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
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();
|
||||
@@ -58,12 +85,42 @@ class SslServerCustomizerTests {
|
||||
.withMessageContaining("com.example.TrustStoreProvider");
|
||||
}
|
||||
|
||||
/**
|
||||
* Null/undefined keystore is not valid unless keystore type is PKCS11.
|
||||
*/
|
||||
@Test
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithNoKeyStoreThrowsWebServerException() {
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() {
|
||||
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() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
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'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
ssl.setKeyStorePassword("1234");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
// Loading the KeyManagerFactory should be successful
|
||||
assertThatNoException().isThrownBy(() -> customizer.getKeyManagerFactory(ssl, null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ 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;
|
||||
|
||||
@@ -29,13 +31,16 @@ 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;
|
||||
|
||||
import org.springframework.boot.testsupport.system.CapturedOutput;
|
||||
import org.springframework.boot.testsupport.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.web.embedded.netty.MockPkcs11SecurityProvider;
|
||||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
@@ -45,6 +50,8 @@ 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.assertThatNoException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@@ -54,15 +61,33 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Brian Clozel
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
@DirtiesUrlFactories
|
||||
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();
|
||||
@@ -176,13 +201,42 @@ class SslConnectorCustomizerTests {
|
||||
assertThat(output).doesNotContain("Password verification failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Null/undefined keystore is invalid unless keystore type is PKCS11.
|
||||
*/
|
||||
@Test
|
||||
void customizeWhenSslIsEnabledWithNoKeyStoreThrowsWebServerException() {
|
||||
void customizeWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() {
|
||||
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() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
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'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
ssl.setKeyStorePassword("1234");
|
||||
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
|
||||
// Loading the KeyManagerFactory should be successful
|
||||
assertThatNoException().isThrownBy(() -> customizer.customize(this.tomcat.getConnector()));
|
||||
}
|
||||
|
||||
private KeyStore loadStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
Resource resource = new ClassPathResource("test.jks");
|
||||
|
||||
@@ -18,26 +18,50 @@ 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.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslBuilderCustomizer}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
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();
|
||||
@@ -76,8 +100,11 @@ class SslBuilderCustomizerTests {
|
||||
.withMessageContaining("com.example.TrustStoreProvider");
|
||||
}
|
||||
|
||||
/**
|
||||
* Null/undefined keystore is invalid unless keystore type is PKCS11.
|
||||
*/
|
||||
@Test
|
||||
void getKeyManagersWhenSslIsEnabledWithNoKeyStoreThrowsWebServerException() throws Exception {
|
||||
void getKeyManagersWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsWebServerException() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
@@ -85,4 +112,33 @@ 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 {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyPassword("password");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> ReflectionTestUtils.invokeMethod(customizer, "getKeyManagers", ssl, null))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class)
|
||||
.withMessageContaining("Input keystore location is not valid for keystore type 'PKCS11'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(PKCS11_PROVIDER.getName());
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user