Browse Source
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-34814
pull/35107/head
12 changed files with 845 additions and 0 deletions
@ -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