Add SSL bundle auto-configuration support
Add auto-configuration for SSL bundles including new configuration properties that can be used to define a bundle. SSL bundle properties are provided under the `spring.ssl.bundle` key. Currently `jks` and `pem` variants are support. Both are configured as a `Map` where the bundle name is the key. A typical example would be: spring: ssl: bundle: pem: mybundle key: password: secret keystore: certificate: classpath:mycert.pem private-key: classpath:mykey.pem A `SslBundleRegistrar` interface is also provided to allow programmatic contributions to the auto-configured `SslBundleRegistry`. See gh-34814pull/35107/head
parent
e3677f7ff6
commit
8e1f24f98f
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<SslBundleRegistrar> sslBundleRegistrars) {
|
||||||
|
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
|
||||||
|
sslBundleRegistrars.forEach((registrar) -> registrar.registerBundles(registry));
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String> ciphers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enabled SSL protocols.
|
||||||
|
*/
|
||||||
|
private Set<String> enabledProtocols;
|
||||||
|
|
||||||
|
public Set<String> getCiphers() {
|
||||||
|
return this.ciphers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCiphers(Set<String> ciphers) {
|
||||||
|
this.ciphers = ciphers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getEnabledProtocols() {
|
||||||
|
return this.enabledProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabledProtocols(Set<String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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<String, PemSslBundleProperties> pem = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java keystore SSL trust material.
|
||||||
|
*/
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private final Map<String, JksSslBundleProperties> jks = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public Map<String, PemSslBundleProperties> getPem() {
|
||||||
|
return this.pem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, JksSslBundleProperties> getJks() {
|
||||||
|
return this.jks;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <P extends SslBundleProperties> void registerBundles(SslBundleRegistry registry, Map<String, P> properties,
|
||||||
|
Function<P, SslBundle> bundleFactory) {
|
||||||
|
properties.forEach((bundleName, bundleProperties) -> registry.registerBundle(bundleName,
|
||||||
|
bundleFactory.apply(bundleProperties)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
@ -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<String> 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<String> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue