diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java new file mode 100644 index 00000000000..ca89bde4a8d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/JksSslBundleProperties.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import org.springframework.boot.ssl.jks.JksSslStoreBundle; + +/** + * {@link SslBundleProperties} for Java keystores. + * + * @author Scott Frederick + * @author Phillip Webb + * @since 3.1.0 + * @see JksSslStoreBundle + */ +public class JksSslBundleProperties extends SslBundleProperties { + + /** + * Keystore properties. + */ + private final Store keystore = new Store(); + + /** + * Truststore properties. + */ + private final Store truststore = new Store(); + + public Store getKeystore() { + return this.keystore; + } + + public Store getTruststore() { + return this.truststore; + } + + /** + * Store properties. + */ + public static class Store { + + /** + * Type of the store to create, e.g. JKS. + */ + private String type; + + /** + * Provider for the store. + */ + private String provider; + + /** + * Location of the resource containing the store content. + */ + private String location; + + /** + * Password used to access the store. + */ + private String password; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getProvider() { + return this.provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java new file mode 100644 index 00000000000..d296ffdec39 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PemSslBundleProperties.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import org.springframework.boot.ssl.pem.PemSslStoreBundle; + +/** + * {@link SslBundleProperties} for PEM-encoded certificates and private keys. + * + * @author Scott Frederick + * @author Phillip Webb + * @since 3.1.0 + * @see PemSslStoreBundle + */ +public class PemSslBundleProperties extends SslBundleProperties { + + /** + * Keystore properties. + */ + private Store keystore = new Store(); + + /** + * Truststore properties. + */ + private Store truststore = new Store(); + + public Store getKeystore() { + return this.keystore; + } + + public Store getTruststore() { + return this.truststore; + } + + /** + * Store properties. + */ + public static class Store { + + /** + * Type of the store to create, e.g. JKS. + */ + String type; + + /** + * Location or content of the certificate in PEM format. + */ + String certificate; + + /** + * Location or content of the private key in PEM format. + */ + String privateKey; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCertificate() { + return this.certificate; + } + + public void setCertificate(String certificate) { + this.certificate = certificate; + } + + public String getPrivateKey() { + return this.privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java new file mode 100644 index 00000000000..337db88f133 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/PropertiesSslBundle.java @@ -0,0 +1,131 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import org.springframework.boot.autoconfigure.ssl.SslBundleProperties.Key; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundleKey; +import org.springframework.boot.ssl.SslManagerBundle; +import org.springframework.boot.ssl.SslOptions; +import org.springframework.boot.ssl.SslStoreBundle; +import org.springframework.boot.ssl.jks.JksSslStoreBundle; +import org.springframework.boot.ssl.jks.JksSslStoreDetails; +import org.springframework.boot.ssl.pem.PemSslStoreBundle; +import org.springframework.boot.ssl.pem.PemSslStoreDetails; + +/** + * {@link SslBundle} backed by {@link JksSslBundleProperties} or + * {@link PemSslBundleProperties}. + * + * @author Scott Frederick + * @author Phillip Webb + * @since 3.1.0 + */ +public final class PropertiesSslBundle implements SslBundle { + + private final SslStoreBundle stores; + + private final SslBundleKey key; + + private final SslOptions options; + + private final String protocol; + + private final SslManagerBundle managers; + + private PropertiesSslBundle(SslStoreBundle stores, SslBundleProperties properties) { + this.stores = stores; + this.key = asSslKeyReference(properties.getKey()); + this.options = asSslOptions(properties.getOptions()); + this.protocol = properties.getProtocol(); + this.managers = SslManagerBundle.from(this.stores, this.key); + } + + private static SslBundleKey asSslKeyReference(Key key) { + return (key != null) ? SslBundleKey.of(key.getPassword(), key.getAlias()) : SslBundleKey.NONE; + } + + private static SslOptions asSslOptions(SslBundleProperties.Options properties) { + return (properties != null) ? SslOptions.of(properties.getCiphers(), properties.getEnabledProtocols()) + : SslOptions.NONE; + } + + @Override + public SslStoreBundle getStores() { + return this.stores; + } + + @Override + public SslBundleKey getKey() { + return this.key; + } + + @Override + public SslOptions getOptions() { + return this.options; + } + + @Override + public String getProtocol() { + return this.protocol; + } + + @Override + public SslManagerBundle getManagers() { + return this.managers; + } + + /** + * Get an {@link SslBundle} for the given {@link PemSslBundleProperties}. + * @param properties the source properties + * @return an {@link SslBundle} instance + */ + public static SslBundle get(PemSslBundleProperties properties) { + return new PropertiesSslBundle(asSslStoreBundle(properties), properties); + } + + /** + * Get an {@link SslBundle} for the given {@link JksSslBundleProperties}. + * @param properties the source properties + * @return an {@link SslBundle} instance + */ + public static SslBundle get(JksSslBundleProperties properties) { + return new PropertiesSslBundle(asSslStoreBundle(properties), properties); + } + + private static SslStoreBundle asSslStoreBundle(PemSslBundleProperties properties) { + PemSslStoreDetails keyStoreDetails = asStoreDetails(properties.getKeystore()); + PemSslStoreDetails trustStoreDetails = asStoreDetails(properties.getTruststore()); + return new PemSslStoreBundle(keyStoreDetails, trustStoreDetails, properties.getKey().getAlias()); + } + + private static PemSslStoreDetails asStoreDetails(PemSslBundleProperties.Store properties) { + return new PemSslStoreDetails(properties.getType(), properties.getCertificate(), properties.getPrivateKey()); + } + + private static SslStoreBundle asSslStoreBundle(JksSslBundleProperties properties) { + JksSslStoreDetails keyStoreDetails = asStoreDetails(properties.getKeystore()); + JksSslStoreDetails trustStoreDetails = asStoreDetails(properties.getTruststore()); + return new JksSslStoreBundle(keyStoreDetails, trustStoreDetails); + } + + private static JksSslStoreDetails asStoreDetails(JksSslBundleProperties.Store properties) { + return new JksSslStoreDetails(properties.getType(), properties.getProvider(), properties.getLocation(), + properties.getPassword()); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.java new file mode 100644 index 00000000000..12b856c8a01 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import java.util.List; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.ssl.DefaultSslBundleRegistry; +import org.springframework.boot.ssl.SslBundleRegistry; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.context.annotation.Bean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for SSL. + * + * @author Scott Frederick + * @since 3.1.0 + */ +@AutoConfiguration +@EnableConfigurationProperties(SslProperties.class) +public class SslAutoConfiguration { + + SslAutoConfiguration() { + } + + @Bean + public SslPropertiesBundleRegistrar sslPropertiesSslBundleRegistrar(SslProperties sslProperties) { + return new SslPropertiesBundleRegistrar(sslProperties); + } + + @Bean + @ConditionalOnMissingBean({ SslBundleRegistry.class, SslBundles.class }) + public DefaultSslBundleRegistry sslBundleRegistry(List sslBundleRegistrars) { + DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry(); + sslBundleRegistrars.forEach((registrar) -> registrar.registerBundles(registry)); + return registry; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java new file mode 100644 index 00000000000..e8b9fd1a4cb --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleProperties.java @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import java.util.Set; + +import org.springframework.boot.ssl.SslBundle; + +/** + * Base class for SSL Bundle properties. + * + * @author Scott Frederick + * @author Phillip Webb + * @since 3.1.0 + * @see SslBundle + */ +public abstract class SslBundleProperties { + + /** + * Key details for the bundle. + */ + private final Key key = new Key(); + + /** + * Options for the SLL connection. + */ + private final Options options = new Options(); + + /** + * SSL Protocol to use. + */ + private String protocol = SslBundle.DEFAULT_PROTOCOL; + + public Key getKey() { + return this.key; + } + + public Options getOptions() { + return this.options; + } + + public String getProtocol() { + return this.protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public static class Options { + + /** + * Supported SSL ciphers. + */ + private Set ciphers; + + /** + * Enabled SSL protocols. + */ + private Set enabledProtocols; + + public Set getCiphers() { + return this.ciphers; + } + + public void setCiphers(Set ciphers) { + this.ciphers = ciphers; + } + + public Set getEnabledProtocols() { + return this.enabledProtocols; + } + + public void setEnabledProtocols(Set enabledProtocols) { + this.enabledProtocols = enabledProtocols; + } + + } + + public static class Key { + + /** + * The password used to access the key in the key store. + */ + private String password; + + /** + * The alias that identifies the key in the key store. + */ + private String alias; + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getAlias() { + return this.alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleRegistrar.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleRegistrar.java new file mode 100644 index 00000000000..ab75cc0a51a --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslBundleRegistrar.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundleRegistry; + +/** + * Interface to be implemented by types that register {@link SslBundle} instances with an + * {@link SslBundleRegistry}. + * + * @author Scott Frederick + * @since 3.1.0 + */ +@FunctionalInterface +public interface SslBundleRegistrar { + + /** + * Callback method for registering {@link SslBundle}s with an + * {@link SslBundleRegistry}. + * @param registry the registry that accepts {@code SslBundle}s + */ + void registerBundles(SslBundleRegistry registry); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslProperties.java new file mode 100644 index 00000000000..ee17a4a64fd --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslProperties.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +/** + * Properties for centralized SSL trust material configuration. + * + * @author Scott Frederick + * @since 3.1.0 + */ +@ConfigurationProperties(prefix = "spring.ssl") +public class SslProperties { + + /** + * SSL bundles. + */ + private final Bundles bundle = new Bundles(); + + public Bundles getBundle() { + return this.bundle; + } + + /** + * Properties to define SSL Bundles. + */ + public static class Bundles { + + /** + * PEM-encoded SSL trust material. + */ + @NestedConfigurationProperty + private final Map pem = new LinkedHashMap<>(); + + /** + * Java keystore SSL trust material. + */ + @NestedConfigurationProperty + private final Map jks = new LinkedHashMap<>(); + + public Map getPem() { + return this.pem; + } + + public Map getJks() { + return this.jks; + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslPropertiesBundleRegistrar.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslPropertiesBundleRegistrar.java new file mode 100644 index 00000000000..89a3e7c1265 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/SslPropertiesBundleRegistrar.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import java.util.Map; +import java.util.function.Function; + +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundleRegistry; + +/** + * A {@link SslBundleRegistrar} that registers SSL bundles based + * {@link SslProperties#getBundle() configuration properties}. + * + * @author Scott Frederick + * @author Phillip Webb + */ +class SslPropertiesBundleRegistrar implements SslBundleRegistrar { + + private final SslProperties.Bundles properties; + + SslPropertiesBundleRegistrar(SslProperties properties) { + this.properties = properties.getBundle(); + } + + @Override + public void registerBundles(SslBundleRegistry registry) { + registerBundles(registry, this.properties.getPem(), PropertiesSslBundle::get); + registerBundles(registry, this.properties.getJks(), PropertiesSslBundle::get); + } + + private

void registerBundles(SslBundleRegistry registry, Map properties, + Function bundleFactory) { + properties.forEach((bundleName, bundleProperties) -> registry.registerBundle(bundleName, + bundleFactory.apply(bundleProperties))); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java new file mode 100644 index 00000000000..c068bf9ff30 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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. + */ + +/** + * Auto-configuration for SSL bundles. + */ +package org.springframework.boot.autoconfigure.ssl; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index c56fa356ab6..ad8a87a6f40 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -262,6 +262,10 @@ "name": "server.shutdown", "defaultValue": "immediate" }, + { + "name": "server.ssl.bundle", + "description": "The name of a configured SSL bundle." + }, { "name": "server.ssl.certificate", "description": "Path to a PEM-encoded SSL certificate file." diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index f688b1590dc..f0018406978 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -116,6 +116,7 @@ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.Reactiv org.springframework.boot.autoconfigure.security.oauth2.server.servlet.OAuth2AuthorizationServerAutoConfiguration org.springframework.boot.autoconfigure.security.oauth2.server.servlet.OAuth2AuthorizationServerJwtAutoConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration +org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfigurationTests.java new file mode 100644 index 00000000000..5717fedfbdf --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ssl/SslAutoConfigurationTests.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012-2023 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.autoconfigure.ssl; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundleRegistry; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SslAutoConfiguration}. + * + * @author Scott Frederick + * @author Phillip Webb + */ +class SslAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class)); + + @Test + void sslBundlesCreatedWithNoConfiguration() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(SslBundleRegistry.class)); + } + + @Test + void sslBundlesCreatedWithCertificates() { + List propertyValues = new ArrayList<>(); + propertyValues.add("spring.ssl.bundle.pem.first.key.alias=alias1"); + propertyValues.add("spring.ssl.bundle.pem.first.key.password=secret1"); + propertyValues.add("spring.ssl.bundle.pem.first.keystore.certificate=cert1.pem"); + propertyValues.add("spring.ssl.bundle.pem.first.keystore.private-key=key1.pem"); + propertyValues.add("spring.ssl.bundle.pem.first.keystore.type=JKS"); + propertyValues.add("spring.ssl.bundle.pem.first.truststore.type=PKCS12"); + propertyValues.add("spring.ssl.bundle.pem.second.key.alias=alias2"); + propertyValues.add("spring.ssl.bundle.pem.second.key.password=secret2"); + propertyValues.add("spring.ssl.bundle.pem.second.keystore.certificate=cert2.pem"); + propertyValues.add("spring.ssl.bundle.pem.second.keystore.private-key=key2.pem"); + propertyValues.add("spring.ssl.bundle.pem.second.keystore.type=PKCS12"); + propertyValues.add("spring.ssl.bundle.pem.second.truststore.certificate=ca.pem"); + propertyValues.add("spring.ssl.bundle.pem.second.truststore.private-key=ca-key.pem"); + propertyValues.add("spring.ssl.bundle.pem.second.truststore.type=JKS"); + this.contextRunner.withPropertyValues(propertyValues.toArray(String[]::new)).run((context) -> { + assertThat(context).hasSingleBean(SslBundles.class); + SslBundles bundles = context.getBean(SslBundles.class); + SslBundle first = bundles.getBundle("first"); + assertThat(first).isNotNull(); + assertThat(first.getStores()).isNotNull(); + assertThat(first.getManagers()).isNotNull(); + assertThat(first.getKey().getAlias()).isEqualTo("alias1"); + assertThat(first.getKey().getPassword()).isEqualTo("secret1"); + assertThat(first.getStores()).extracting("keyStoreDetails").extracting("type").isEqualTo("JKS"); + assertThat(first.getStores()).extracting("trustStoreDetails").extracting("type").isEqualTo("PKCS12"); + SslBundle second = bundles.getBundle("second"); + assertThat(second).isNotNull(); + assertThat(second.getStores()).isNotNull(); + assertThat(second.getManagers()).isNotNull(); + assertThat(second.getKey().getAlias()).isEqualTo("alias2"); + assertThat(second.getKey().getPassword()).isEqualTo("secret2"); + assertThat(second.getStores()).extracting("keyStoreDetails").extracting("type").isEqualTo("PKCS12"); + assertThat(second.getStores()).extracting("trustStoreDetails").extracting("type").isEqualTo("JKS"); + }); + } + + @Test + void sslBundlesCreatedWithCustomSslBundle() { + List propertyValues = new ArrayList<>(); + propertyValues.add("custom.ssl.key.alias=alias1"); + propertyValues.add("custom.ssl.key.password=secret1"); + propertyValues.add("custom.ssl.keystore.type=JKS"); + propertyValues.add("custom.ssl.truststore.type=PKCS12"); + this.contextRunner.withUserConfiguration(CustomSslBundleConfiguration.class) + .withPropertyValues(propertyValues.toArray(String[]::new)) + .run((context) -> { + assertThat(context).hasSingleBean(SslBundles.class); + SslBundles bundles = context.getBean(SslBundles.class); + SslBundle first = bundles.getBundle("custom"); + assertThat(first).isNotNull(); + assertThat(first.getStores()).isNotNull(); + assertThat(first.getManagers()).isNotNull(); + assertThat(first.getKey().getAlias()).isEqualTo("alias1"); + assertThat(first.getKey().getPassword()).isEqualTo("secret1"); + assertThat(first.getStores()).extracting("keyStoreDetails").extracting("type").isEqualTo("JKS"); + assertThat(first.getStores()).extracting("trustStoreDetails").extracting("type").isEqualTo("PKCS12"); + }); + } + + @Configuration + @EnableConfigurationProperties(CustomSslProperties.class) + public static class CustomSslBundleConfiguration { + + @Bean + public SslBundleRegistrar customSslBundlesRegistrar(CustomSslProperties properties) { + return new CustomSslBundlesRegistrar(properties); + } + + } + + @ConfigurationProperties("custom.ssl") + static class CustomSslProperties extends PemSslBundleProperties { + + } + + static class CustomSslBundlesRegistrar implements SslBundleRegistrar { + + private final CustomSslProperties properties; + + CustomSslBundlesRegistrar(CustomSslProperties properties) { + this.properties = properties; + } + + @Override + public void registerBundles(SslBundleRegistry registry) { + registry.registerBundle("custom", PropertiesSslBundle.get(this.properties)); + } + + } + +}