diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java index 205aa85ca5e..85d09dd4e04 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java @@ -58,6 +58,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Scope; import org.springframework.core.io.Resource; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -158,7 +159,8 @@ public class CassandraAutoConfiguration { private void configureSsl(CqlSessionBuilder builder, SslBundle sslBundle) { SslOptions options = sslBundle.getOptions(); - String[] ciphers = (options.getCiphers() != null) ? options.getCiphers().toArray(String[]::new) : null; + Assert.state(options.getEnabledProtocols() == null, "SSL protocol options cannot be specified with Cassandra"); + String[] ciphers = SslOptions.toArray(options.getCiphers()); builder.withSslEngineFactory(new ProgrammaticSslEngineFactory(sslBundle.createSslContext(), ciphers)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java index 15bd883fef5..89522bb1ea8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java @@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.CouchbaseCondition; +import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Ssl; import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -50,7 +51,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; +import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for Couchbase. @@ -125,10 +128,14 @@ public class CouchbaseAutoConfiguration { } private void configureSsl(Builder builder, SslBundles sslBundles) { + Ssl sslProperties = this.properties.getEnv().getSsl(); + SslBundle sslBundle = (StringUtils.hasText(sslProperties.getBundle())) + ? sslBundles.getBundle(sslProperties.getBundle()) : null; + Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(), + "SSL Options cannot be specified with Couchbase"); builder.securityConfig((config) -> { config.enableTls(true); - TrustManagerFactory trustManagerFactory = getTrustManagerFactory(this.properties.getEnv().getSsl(), - sslBundles); + TrustManagerFactory trustManagerFactory = getTrustManagerFactory(sslProperties, sslBundle); if (trustManagerFactory != null) { config.trustManagerFactory(trustManagerFactory); } @@ -136,15 +143,11 @@ public class CouchbaseAutoConfiguration { } @SuppressWarnings("removal") - private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl ssl, SslBundles sslBundles) { - if (ssl.getKeyStore() != null) { - return loadTrustManagerFactory(ssl); + private TrustManagerFactory getTrustManagerFactory(CouchbaseProperties.Ssl sslProperties, SslBundle sslBundle) { + if (sslProperties.getKeyStore() != null) { + return loadTrustManagerFactory(sslProperties); } - if (ssl.getBundle() != null) { - SslBundle bundle = sslBundles.getBundle(ssl.getBundle()); - return bundle.getManagers().getTrustManagerFactory(); - } - return null; + return (sslBundle != null) ? sslBundle.getManagers().getTrustManagerFactory() : null; } @SuppressWarnings("removal") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java index f22fcef11cc..0b2aaf1e85f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations.java @@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.elasticsearch; import java.net.URI; import java.time.Duration; import java.util.List; -import java.util.Set; import java.util.stream.Stream; import javax.net.ssl.HostnameVerifier; @@ -117,16 +116,12 @@ class ElasticsearchRestClientConfigurations { private void configureSsl(HttpAsyncClientBuilder httpClientBuilder, SslBundle sslBundle) { SSLContext sslcontext = sslBundle.createSslContext(); SslOptions sslOptions = sslBundle.getOptions(); - String[] enabledProtocols = toArray(sslOptions.getEnabledProtocols()); - String[] ciphers = toArray(sslOptions.getCiphers()); + String[] enabledProtocols = SslOptions.toArray(sslOptions.getEnabledProtocols()); + String[] ciphers = SslOptions.toArray(sslOptions.getCiphers()); httpClientBuilder.setSSLStrategy( new SSLIOSessionStrategy(sslcontext, enabledProtocols, ciphers, (HostnameVerifier) null)); } - private static String[] toArray(Set set) { - return (set != null) ? set.toArray(String[]::new) : null; - } - } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java index 0d3478871e1..12e604491d3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/StandardMongoClientSettingsBuilderCustomizer.java @@ -24,6 +24,7 @@ import org.bson.UuidRepresentation; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.core.Ordered; +import org.springframework.util.Assert; /** * A {@link MongoClientSettingsBuilderCustomizer} that applies standard settings to a @@ -67,6 +68,7 @@ public class StandardMongoClientSettingsBuilderCustomizer implements MongoClient settings.enabled(true); if (this.ssl.getBundle() != null) { SslBundle sslBundle = this.sslBundles.getBundle(this.ssl.getBundle()); + Assert.state(!sslBundle.getOptions().isSpecified(), "SSL options cannot be specified with MongoDB"); settings.context(sslBundle.createSslContext()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/HttpComponentsClientHttpConnectorFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/HttpComponentsClientHttpConnectorFactory.java index f63ed8b93eb..3a895239c28 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/HttpComponentsClientHttpConnectorFactory.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/HttpComponentsClientHttpConnectorFactory.java @@ -52,10 +52,10 @@ class HttpComponentsClientHttpConnectorFactory @Override public TlsDetails verify(NamedEndpoint endpoint, SSLEngine sslEngine) throws SSLException { if (options.getCiphers() != null) { - sslEngine.setEnabledCipherSuites(options.getCiphers().toArray(String[]::new)); + sslEngine.setEnabledCipherSuites(SslOptions.toArray(options.getCiphers())); } if (options.getEnabledProtocols() != null) { - sslEngine.setEnabledProtocols(options.getEnabledProtocols().toArray(String[]::new)); + sslEngine.setEnabledProtocols(SslOptions.toArray(options.getEnabledProtocols())); } return null; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JdkClientHttpConnectorFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JdkClientHttpConnectorFactory.java index 75823180447..2688e647e79 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JdkClientHttpConnectorFactory.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JdkClientHttpConnectorFactory.java @@ -18,14 +18,13 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client; import java.net.http.HttpClient; import java.net.http.HttpClient.Builder; -import java.util.Set; import javax.net.ssl.SSLParameters; import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslOptions; import org.springframework.http.client.reactive.JdkClientHttpConnector; import org.springframework.http.client.reactive.JettyClientHttpConnector; -import org.springframework.util.CollectionUtils; /** * {@link ClientHttpConnectorFactory} for {@link JettyClientHttpConnector}. @@ -38,17 +37,14 @@ class JdkClientHttpConnectorFactory implements ClientHttpConnectorFactory set) { - return (CollectionUtils.isEmpty(set)) ? null : set.toArray(String[]::new); - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java index 4335ccb1469..46ae4b797f3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java @@ -46,11 +46,11 @@ class JettyClientHttpConnectorFactory implements ClientHttpConnectorFactory) null, (Set) null); + /** + * Return if any SSL options have been specified. + * @return {@true} if SSL options have been specified + */ + default boolean isSpecified() { + return (getCiphers() != null) && (getEnabledProtocols() != null); + } + /** * Return the ciphers that can be used or an empty set. The cipher names in this set * should be compatible with those supported by @@ -86,6 +95,16 @@ public interface SslOptions { } + /** + * Helper method that provides a null-safe way to convert a {@link Collection} to a + * {@code String[]} for client libraries to use. + * @param collection the collection to convert + * @return a string array or {@code null} + */ + static String[] toArray(Collection collection) { + return (collection != null) ? collection.toArray(String[]::new) : null; + } + private static Set asSet(String[] array) { return (array != null) ? Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(array))) : null; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java index e1994554771..ecaecd25b21 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java @@ -22,7 +22,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.time.Duration; -import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -172,8 +171,8 @@ public final class ClientHttpRequestFactories { } if (sslBundle != null) { SslOptions options = sslBundle.getOptions(); - String[] enabledProtocols = toArray(options.getEnabledProtocols()); - String[] ciphers = toArray(options.getCiphers()); + String[] enabledProtocols = SslOptions.toArray(options.getEnabledProtocols()); + String[] ciphers = SslOptions.toArray(options.getCiphers()); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslBundle.createSslContext(), enabledProtocols, ciphers, new DefaultHostnameVerifier()); connectionManagerBuilder.setSSLSocketFactory(socketFactory); @@ -182,10 +181,6 @@ public final class ClientHttpRequestFactories { return HttpClientBuilder.create().setConnectionManager(connectionManager).build(); } - private static String[] toArray(Set set) { - return (set != null) ? set.toArray(String[]::new) : null; - } - } /** @@ -205,6 +200,7 @@ public final class ClientHttpRequestFactories { private static OkHttp3ClientHttpRequestFactory createRequestFactory(SslBundle sslBundle) { if (sslBundle != null) { + Assert.state(!sslBundle.getOptions().isSpecified(), "SSL Options cannot be specified with OkHttp"); SSLSocketFactory socketFactory = sslBundle.createSslContext().getSocketFactory(); TrustManager[] trustManagers = sslBundle.getManagers().getTrustManagers(); Assert.state(trustManagers.length == 1, @@ -228,6 +224,8 @@ public final class ClientHttpRequestFactories { SslBundle sslBundle = settings.sslBundle(); SimpleClientHttpRequestFactory requestFactory = (sslBundle != null) ? new SimpleClientHttpsRequestFactory(sslBundle) : new SimpleClientHttpRequestFactory(); + Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(), + "SSL Options cannot be specified with Java connections"); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout); map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout); 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 c42250c774a..22a5ead2b66 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 @@ -179,11 +179,12 @@ class SslServerCustomizer implements JettyServerCustomizer { } factory.setCertAlias(key.getAlias()); if (options.getCiphers() != null) { - factory.setIncludeCipherSuites(options.getCiphers().toArray(String[]::new)); + factory.setIncludeCipherSuites(SslOptions.toArray(options.getCiphers())); factory.setExcludeCipherSuites(); } if (options.getEnabledProtocols() != null) { - factory.setIncludeProtocols(options.getEnabledProtocols().toArray(String[]::new)); + factory.setIncludeProtocols(SslOptions.toArray(options.getEnabledProtocols())); + factory.setExcludeProtocols(); } try { if (key.getPassword() != null) {