Refactor web server support to use SslBundles
Update Tomcat, Jetty, Undertow and Netty servers so that an SslBundle is used to apply SSL configuration. Existing `Ssl` properties are internally adapted to an `SslBundle` using the `WebServerSslBundle` class. Additionally, if `Ssl.getBundle()` returns a non-null value the the `SslBundles` bean will be used to find a registered bundle by name. See gh-34814pull/35107/head
parent
8e1f24f98f
commit
66db13b962
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Parser for X.509 certificates in PEM format.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class CertificateParser {
|
||||
|
||||
private static final String HEADER = "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String BASE64_TEXT = "([a-z0-9+/=\\r\\n]+)";
|
||||
|
||||
private static final String FOOTER = "-+END\\s+.*CERTIFICATE[^-]*-+";
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile(HEADER + BASE64_TEXT + FOOTER, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private CertificateParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load certificates from the specified resource.
|
||||
* @param path the certificate to parse
|
||||
* @return the parsed certificates
|
||||
*/
|
||||
static X509Certificate[] parse(String path) {
|
||||
CertificateFactory factory = getCertificateFactory();
|
||||
List<X509Certificate> certificates = new ArrayList<>();
|
||||
readCertificates(path, factory, certificates::add);
|
||||
return certificates.toArray(new X509Certificate[0]);
|
||||
}
|
||||
|
||||
private static CertificateFactory getCertificateFactory() {
|
||||
try {
|
||||
return CertificateFactory.getInstance("X.509");
|
||||
}
|
||||
catch (CertificateException ex) {
|
||||
throw new IllegalStateException("Unable to get X.509 certificate factory", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void readCertificates(String resource, CertificateFactory factory,
|
||||
Consumer<X509Certificate> consumer) {
|
||||
try {
|
||||
String text = readText(resource);
|
||||
Matcher matcher = PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
String encodedText = matcher.group(1);
|
||||
byte[] decodedBytes = decodeBase64(encodedText);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedBytes);
|
||||
while (inputStream.available() > 0) {
|
||||
consumer.accept((X509Certificate) factory.generateCertificate(inputStream));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CertificateException | IOException ex) {
|
||||
throw new IllegalStateException("Error reading certificate from '" + resource + "' : " + ex.getMessage(),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readText(String resource) throws IOException {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (Reader reader = new InputStreamReader(url.openStream())) {
|
||||
return FileCopyUtils.copyToString(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(String content) {
|
||||
byte[] bytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes();
|
||||
return Base64.getDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link SslStoreProvider} that creates key and trust stores from Java keystore files.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
final class JavaKeyStoreSslStoreProvider implements SslStoreProvider {
|
||||
|
||||
private final Ssl ssl;
|
||||
|
||||
private JavaKeyStoreSslStoreProvider(Ssl ssl) {
|
||||
this.ssl = ssl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStore getKeyStore() throws Exception {
|
||||
return createKeyStore(this.ssl.getKeyStoreType(), this.ssl.getKeyStoreProvider(), this.ssl.getKeyStore(),
|
||||
this.ssl.getKeyStorePassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStore getTrustStore() throws Exception {
|
||||
if (this.ssl.getTrustStore() == null) {
|
||||
return null;
|
||||
}
|
||||
return createKeyStore(this.ssl.getTrustStoreType(), this.ssl.getTrustStoreProvider(), this.ssl.getTrustStore(),
|
||||
this.ssl.getTrustStorePassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyPassword() {
|
||||
return this.ssl.getKeyPassword();
|
||||
}
|
||||
|
||||
private KeyStore createKeyStore(String type, String provider, String location, String password) throws Exception {
|
||||
type = (type != null) ? type : "JKS";
|
||||
char[] passwordChars = (password != null) ? password.toCharArray() : null;
|
||||
KeyStore store = (provider != null) ? KeyStore.getInstance(type, provider) : KeyStore.getInstance(type);
|
||||
if (type.equalsIgnoreCase("PKCS11")) {
|
||||
Assert.state(!StringUtils.hasText(location),
|
||||
() -> "KeyStore location is '" + location + "', but must be empty or null for PKCS11 key stores");
|
||||
store.load(null, passwordChars);
|
||||
}
|
||||
else {
|
||||
Assert.state(StringUtils.hasText(location), () -> "KeyStore location must not be empty or null");
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(location);
|
||||
try (InputStream stream = url.openStream()) {
|
||||
store.load(stream, passwordChars);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Could not load key store '" + location + "'", ex);
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link SslStoreProvider} if the appropriate SSL properties are
|
||||
* configured.
|
||||
* @param ssl the SSL properties
|
||||
* @return an {@code SslStoreProvider} or {@code null}
|
||||
*/
|
||||
static SslStoreProvider from(Ssl ssl) {
|
||||
if (ssl != null && ssl.isEnabled()) {
|
||||
return new JavaKeyStoreSslStoreProvider(ssl);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Parser for PKCS private key files in PEM format.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class PrivateKeyParser {
|
||||
|
||||
private static final String PKCS1_HEADER = "-+BEGIN\\s+RSA\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String PKCS1_FOOTER = "-+END\\s+RSA\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String PKCS8_HEADER = "-+BEGIN\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String PKCS8_FOOTER = "-+END\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String EC_HEADER = "-+BEGIN\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String EC_FOOTER = "-+END\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String BASE64_TEXT = "([a-z0-9+/=\\r\\n]+)";
|
||||
|
||||
private static final List<PemParser> PEM_PARSERS;
|
||||
static {
|
||||
List<PemParser> parsers = new ArrayList<>();
|
||||
parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, "RSA", PrivateKeyParser::createKeySpecForPkcs1));
|
||||
parsers.add(new PemParser(EC_HEADER, EC_FOOTER, "EC", PrivateKeyParser::createKeySpecForEc));
|
||||
parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, "RSA", PKCS8EncodedKeySpec::new));
|
||||
PEM_PARSERS = Collections.unmodifiableList(parsers);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 encoded object identifier {@literal 1.2.840.113549.1.1.1}.
|
||||
*/
|
||||
private static final int[] RSA_ALGORITHM = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
|
||||
|
||||
/**
|
||||
* ASN.1 encoded object identifier {@literal 1.2.840.10045.2.1}.
|
||||
*/
|
||||
private static final int[] EC_ALGORITHM = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 };
|
||||
|
||||
/**
|
||||
* ASN.1 encoded object identifier {@literal 1.3.132.0.34}.
|
||||
*/
|
||||
private static final int[] EC_PARAMETERS = { 0x2b, 0x81, 0x04, 0x00, 0x22 };
|
||||
|
||||
private PrivateKeyParser() {
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1(byte[] bytes) {
|
||||
return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForEc(byte[] bytes) {
|
||||
return createKeySpecForAlgorithm(bytes, EC_ALGORITHM, EC_PARAMETERS);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForAlgorithm(byte[] bytes, int[] algorithm, int[] parameters) {
|
||||
try {
|
||||
DerEncoder encoder = new DerEncoder();
|
||||
encoder.integer(0x00); // Version 0
|
||||
DerEncoder algorithmIdentifier = new DerEncoder();
|
||||
algorithmIdentifier.objectIdentifier(algorithm);
|
||||
algorithmIdentifier.objectIdentifier(parameters);
|
||||
byte[] byteArray = algorithmIdentifier.toByteArray();
|
||||
encoder.sequence(byteArray);
|
||||
encoder.octetString(bytes);
|
||||
return new PKCS8EncodedKeySpec(encoder.toSequence());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a private key from the specified resource.
|
||||
* @param resource the private key to parse
|
||||
* @return the parsed private key
|
||||
*/
|
||||
static PrivateKey parse(String resource) {
|
||||
try {
|
||||
String text = readText(resource);
|
||||
for (PemParser pemParser : PEM_PARSERS) {
|
||||
PrivateKey privateKey = pemParser.parse(text);
|
||||
if (privateKey != null) {
|
||||
return privateKey;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unrecognized private key format");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Error loading private key file " + resource, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readText(String resource) throws IOException {
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
try (Reader reader = new InputStreamReader(url.openStream())) {
|
||||
return FileCopyUtils.copyToString(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser for a specific PEM format.
|
||||
*/
|
||||
private static class PemParser {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory;
|
||||
|
||||
PemParser(String header, String footer, String algorithm,
|
||||
Function<byte[], PKCS8EncodedKeySpec> keySpecFactory) {
|
||||
this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE);
|
||||
this.algorithm = algorithm;
|
||||
this.keySpecFactory = keySpecFactory;
|
||||
}
|
||||
|
||||
PrivateKey parse(String text) {
|
||||
Matcher matcher = this.pattern.matcher(text);
|
||||
return (!matcher.find()) ? null : parse(decodeBase64(matcher.group(1)));
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(String content) {
|
||||
byte[] contentBytes = content.replaceAll("\r", "").replaceAll("\n", "").getBytes();
|
||||
return Base64.getDecoder().decode(contentBytes);
|
||||
}
|
||||
|
||||
private PrivateKey parse(byte[] bytes) {
|
||||
try {
|
||||
PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(this.algorithm);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
catch (GeneralSecurityException ex) {
|
||||
throw new IllegalArgumentException("Unexpected key format", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple ASN.1 DER encoder.
|
||||
*/
|
||||
static class DerEncoder {
|
||||
|
||||
private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
void objectIdentifier(int... encodedObjectIdentifier) throws IOException {
|
||||
int code = (encodedObjectIdentifier != null) ? 0x06 : 0x05;
|
||||
codeLengthBytes(code, bytes(encodedObjectIdentifier));
|
||||
}
|
||||
|
||||
void integer(int... encodedInteger) throws IOException {
|
||||
codeLengthBytes(0x02, bytes(encodedInteger));
|
||||
}
|
||||
|
||||
void octetString(byte[] bytes) throws IOException {
|
||||
codeLengthBytes(0x04, bytes);
|
||||
}
|
||||
|
||||
void sequence(int... elements) throws IOException {
|
||||
sequence(bytes(elements));
|
||||
}
|
||||
|
||||
void sequence(byte[] bytes) throws IOException {
|
||||
codeLengthBytes(0x30, bytes);
|
||||
}
|
||||
|
||||
void codeLengthBytes(int code, byte[] bytes) throws IOException {
|
||||
this.stream.write(code);
|
||||
int length = (bytes != null) ? bytes.length : 0;
|
||||
if (length <= 127) {
|
||||
this.stream.write(length & 0xFF);
|
||||
}
|
||||
else {
|
||||
ByteArrayOutputStream lengthStream = new ByteArrayOutputStream();
|
||||
while (length != 0) {
|
||||
lengthStream.write(length & 0xFF);
|
||||
length = length >> 8;
|
||||
}
|
||||
byte[] lengthBytes = lengthStream.toByteArray();
|
||||
this.stream.write(0x80 | lengthBytes.length);
|
||||
for (int i = lengthBytes.length - 1; i >= 0; i--) {
|
||||
this.stream.write(lengthBytes[i]);
|
||||
}
|
||||
}
|
||||
if (bytes != null) {
|
||||
this.stream.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] bytes(int... elements) {
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] result = new byte[elements.length];
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
result[i] = (byte) elements[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] toSequence() throws IOException {
|
||||
DerEncoder sequenceEncoder = new DerEncoder();
|
||||
sequenceEncoder.sequence(toByteArray());
|
||||
return sequenceEncoder.toByteArray();
|
||||
}
|
||||
|
||||
byte[] toByteArray() {
|
||||
return this.stream.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
/**
|
||||
* Creates an {@link SslStoreProvider} based on SSL configuration properties.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public final class SslStoreProviderFactory {
|
||||
|
||||
private SslStoreProviderFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link SslStoreProvider} if the appropriate SSL properties are
|
||||
* configured.
|
||||
* @param ssl the SSL properties
|
||||
* @return an {@code SslStoreProvider} or {@code null}
|
||||
*/
|
||||
public static SslStoreProvider from(Ssl ssl) {
|
||||
SslStoreProvider sslStoreProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
return ((sslStoreProvider != null) ? sslStoreProvider : JavaKeyStoreSslStoreProvider.from(ssl));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.security.KeyStore;
|
||||
|
||||
import org.springframework.boot.ssl.NoSuchSslBundleException;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundleKey;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
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;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
/**
|
||||
* {@link SslBundle} backed by {@link Ssl} or an {@link SslStoreProvider}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public final class WebServerSslBundle implements SslBundle {
|
||||
|
||||
private final SslStoreBundle stores;
|
||||
|
||||
private final SslBundleKey key;
|
||||
|
||||
private final SslOptions options;
|
||||
|
||||
private final String protocol;
|
||||
|
||||
private final SslManagerBundle managers;
|
||||
|
||||
private WebServerSslBundle(SslStoreBundle stores, String keyPassword, Ssl ssl) {
|
||||
this.stores = stores;
|
||||
this.key = SslBundleKey.of(keyPassword, ssl.getKeyAlias());
|
||||
this.protocol = ssl.getProtocol();
|
||||
this.options = SslOptions.of(ssl.getCiphers(), ssl.getEnabledProtocols());
|
||||
this.managers = SslManagerBundle.from(this.stores, this.key);
|
||||
}
|
||||
|
||||
private static SslStoreBundle createPemStoreBundle(Ssl ssl) {
|
||||
PemSslStoreDetails keyStoreDetails = new PemSslStoreDetails(ssl.getKeyStoreType(), ssl.getCertificate(),
|
||||
ssl.getCertificatePrivateKey());
|
||||
PemSslStoreDetails trustStoreDetails = new PemSslStoreDetails(ssl.getTrustStoreType(),
|
||||
ssl.getTrustCertificate(), ssl.getTrustCertificatePrivateKey());
|
||||
return new PemSslStoreBundle(keyStoreDetails, trustStoreDetails, ssl.getKeyAlias());
|
||||
}
|
||||
|
||||
private static SslStoreBundle createJksStoreBundle(Ssl ssl) {
|
||||
JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails(ssl.getKeyStoreType(), ssl.getKeyStoreProvider(),
|
||||
ssl.getKeyStore(), ssl.getKeyStorePassword());
|
||||
JksSslStoreDetails trustStoreDetails = new JksSslStoreDetails(ssl.getTrustStoreType(),
|
||||
ssl.getTrustStoreProvider(), ssl.getTrustStore(), ssl.getTrustStorePassword());
|
||||
return new JksSslStoreBundle(keyStoreDetails, trustStoreDetails);
|
||||
}
|
||||
|
||||
@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 the {@link SslBundle} that should be used for the given {@link Ssl} instance.
|
||||
* @param ssl the source ssl instance
|
||||
* @return a {@link SslBundle} instance
|
||||
* @throws NoSuchSslBundleException if a bundle lookup fails
|
||||
*/
|
||||
public static SslBundle get(Ssl ssl) throws NoSuchSslBundleException {
|
||||
return get(ssl, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link SslBundle} that should be used for the given {@link Ssl} instance.
|
||||
* @param ssl the source ssl instance
|
||||
* @param sslBundles the bundles that should be used when {@link Ssl#getBundle()} is
|
||||
* set
|
||||
* @return a {@link SslBundle} instance
|
||||
* @throws NoSuchSslBundleException if a bundle lookup fails
|
||||
*/
|
||||
public static SslBundle get(Ssl ssl, SslBundles sslBundles) throws NoSuchSslBundleException {
|
||||
return get(ssl, sslBundles, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link SslBundle} that should be used for the given {@link Ssl} and
|
||||
* {@link SslStoreProvider} instances.
|
||||
* @param ssl the source {@link Ssl} instance
|
||||
* @param sslBundles the bundles that should be used when {@link Ssl#getBundle()} is
|
||||
* set
|
||||
* @param sslStoreProvider the {@link SslStoreProvider} to use or {@code null}
|
||||
* @return a {@link SslBundle} instance
|
||||
* @throws NoSuchSslBundleException if a bundle lookup fails
|
||||
* @deprecated since 3.1.0 for removal in 3.3.0 along with {@link SslStoreProvider}
|
||||
*/
|
||||
@Deprecated(since = "3.1.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
public static SslBundle get(Ssl ssl, SslBundles sslBundles, SslStoreProvider sslStoreProvider) {
|
||||
Assert.state(Ssl.isEnabled(ssl), "SSL is not enabled");
|
||||
String keyPassword = (sslStoreProvider != null) ? sslStoreProvider.getKeyPassword() : null;
|
||||
keyPassword = (keyPassword != null) ? keyPassword : ssl.getKeyPassword();
|
||||
if (sslStoreProvider != null) {
|
||||
SslStoreBundle stores = new SslStoreProviderBundleAdapter(sslStoreProvider);
|
||||
return new WebServerSslBundle(stores, keyPassword, ssl);
|
||||
}
|
||||
String bundleName = ssl.getBundle();
|
||||
if (StringUtils.hasText(bundleName)) {
|
||||
Assert.state(sslBundles != null,
|
||||
() -> "SSL bundle '%s' was requested but no SslBundles instance was provided"
|
||||
.formatted(bundleName));
|
||||
return sslBundles.getBundle(bundleName);
|
||||
}
|
||||
SslStoreBundle stores = createStoreBundle(ssl);
|
||||
return new WebServerSslBundle(stores, keyPassword, ssl);
|
||||
}
|
||||
|
||||
private static SslStoreBundle createStoreBundle(Ssl ssl) {
|
||||
if (hasCertificateProperties(ssl)) {
|
||||
return createPemStoreBundle(ssl);
|
||||
}
|
||||
if (hasJavaKeyStoreProperties(ssl)) {
|
||||
return createJksStoreBundle(ssl);
|
||||
}
|
||||
throw new IllegalStateException("SSL is enabled but no trust material is configured");
|
||||
}
|
||||
|
||||
static SslBundle createCertificateFileSslStoreProviderDelegate(Ssl ssl) {
|
||||
if (!hasCertificateProperties(ssl)) {
|
||||
return null;
|
||||
}
|
||||
SslStoreBundle stores = createPemStoreBundle(ssl);
|
||||
return new WebServerSslBundle(stores, ssl.getKeyPassword(), ssl);
|
||||
}
|
||||
|
||||
private static boolean hasCertificateProperties(Ssl ssl) {
|
||||
return Ssl.isEnabled(ssl) && ssl.getCertificate() != null && ssl.getCertificatePrivateKey() != null;
|
||||
}
|
||||
|
||||
private static boolean hasJavaKeyStoreProperties(Ssl ssl) {
|
||||
return Ssl.isEnabled(ssl) && ssl.getKeyStore() != null
|
||||
|| (ssl.getKeyStoreType() != null && ssl.getKeyStoreType().equals("PKCS11"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to adapt a {@link SslStoreProvider} into a {@link SslStoreBundle}.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
private static class SslStoreProviderBundleAdapter implements SslStoreBundle {
|
||||
|
||||
private final SslStoreProvider sslStoreProvider;
|
||||
|
||||
SslStoreProviderBundleAdapter(SslStoreProvider sslStoreProvider) {
|
||||
this.sslStoreProvider = sslStoreProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStore getKeyStore() {
|
||||
return ThrowingSupplier.of(this.sslStoreProvider::getKeyStore).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyStorePassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStore getTrustStore() {
|
||||
return ThrowingSupplier.of(this.sslStoreProvider::getTrustStore).get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* 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.web.embedded.netty;
|
||||
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.embedded.test.MockPkcs11Security;
|
||||
import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProviderFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslServerCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@MockPkcs11Security
|
||||
class SslServerCustomizerTests {
|
||||
|
||||
@Test
|
||||
void keyStoreProviderIsUsedWhenCreatingKeyStore() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyStoreProvider("com.example.KeyStoreProvider");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagerFactory(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(NoSuchProviderException.class)
|
||||
.withMessageContaining("com.example.KeyStoreProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
void trustStoreProviderIsUsedWhenCreatingTrustStore() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setTrustStorePassword("password");
|
||||
ssl.setTrustStore("src/test/resources/test.jks");
|
||||
ssl.setTrustStoreProvider("com.example.TrustStoreProvider");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getTrustManagerFactory(SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(NoSuchProviderException.class)
|
||||
.withMessageContaining("com.example.TrustStoreProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagerFactory(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("KeyStore location must not be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyPassword("password");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagerFactory(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("must be empty or null for PKCS11 key stores");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyManagerFactoryWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
|
||||
ssl.setKeyStorePassword("1234");
|
||||
SslServerCustomizer customizer = new SslServerCustomizer(ssl, null, null);
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> customizer.getKeyManagerFactory(ssl, SslStoreProviderFactory.from(ssl)));
|
||||
}
|
||||
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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.web.embedded.undertow;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.embedded.test.MockPkcs11Security;
|
||||
import org.springframework.boot.web.embedded.test.MockPkcs11SecurityProvider;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProviderFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslBuilderCustomizer}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Raheela Aslam
|
||||
* @author Cyril Dangerville
|
||||
*/
|
||||
@MockPkcs11Security
|
||||
class SslBuilderCustomizerTests {
|
||||
|
||||
@Test
|
||||
void getKeyManagersWhenAliasIsNullShouldNotDecorate() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
KeyManager[] keyManagers = customizer.getKeyManagers(ssl, SslStoreProviderFactory.from(ssl));
|
||||
Class<?> name = Class
|
||||
.forName("org.springframework.boot.web.embedded.undertow.SslBuilderCustomizer$ConfigurableAliasKeyManager");
|
||||
assertThat(keyManagers[0]).isNotInstanceOf(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keyStoreProviderIsUsedWhenCreatingKeyStore() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyStoreProvider("com.example.KeyStoreProvider");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagers(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(NoSuchProviderException.class)
|
||||
.withMessageContaining("com.example.KeyStoreProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
void trustStoreProviderIsUsedWhenCreatingTrustStore() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setTrustStorePassword("password");
|
||||
ssl.setTrustStore("src/test/resources/test.jks");
|
||||
ssl.setTrustStoreProvider("com.example.TrustStoreProvider");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getTrustManagers(SslStoreProviderFactory.from(ssl)))
|
||||
.withMessageContaining("com.example.TrustStoreProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyManagersWhenSslIsEnabledWithNoKeyStoreAndNotPkcs11ThrowsException() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagers(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("KeyStore location must not be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void configureSslWhenSslIsEnabledWithPkcs11AndKeyStoreThrowsException() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
ssl.setKeyPassword("password");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> customizer.getKeyManagers(ssl, SslStoreProviderFactory.from(ssl)))
|
||||
.withCauseInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("must be empty or null for PKCS11 key stores");
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeWhenSslIsEnabledWithPkcs11AndKeyStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME);
|
||||
ssl.setKeyStorePassword("1234");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
assertThatNoException().isThrownBy(() -> customizer.getKeyManagers(ssl, SslStoreProviderFactory.from(ssl)));
|
||||
}
|
||||
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.server;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CertificateFileSslStoreProvider}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class CertificateFileSslStoreProviderTests {
|
||||
|
||||
@Test
|
||||
void fromSslWhenNullReturnsNull() {
|
||||
assertThat(CertificateFileSslStoreProvider.from(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWhenDisabledReturnsNull() {
|
||||
assertThat(CertificateFileSslStoreProvider.from(new Ssl())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWithCertAndKeyReturnsStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(true);
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
SslStoreProvider storeProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), KeyStore.getDefaultType(), "spring-boot-web");
|
||||
assertThat(storeProvider.getTrustStore()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWithCertAndKeyAndTrustCertReturnsStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(true);
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
ssl.setTrustCertificate("classpath:test-cert.pem");
|
||||
SslStoreProvider storeProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), KeyStore.getDefaultType(), "spring-boot-web");
|
||||
assertStoreContainsCert(storeProvider.getTrustStore(), KeyStore.getDefaultType(), "spring-boot-web-0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWithCertAndKeyAndTrustCertAndTrustKeyReturnsStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(true);
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
ssl.setTrustCertificate("classpath:test-cert.pem");
|
||||
ssl.setTrustCertificatePrivateKey("classpath:test-key.pem");
|
||||
SslStoreProvider storeProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), KeyStore.getDefaultType(), "spring-boot-web");
|
||||
assertStoreContainsCertAndKey(storeProvider.getTrustStore(), KeyStore.getDefaultType(), "spring-boot-web");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWithKeyAliasReturnsStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(true);
|
||||
ssl.setKeyAlias("test-alias");
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
ssl.setTrustCertificate("classpath:test-cert.pem");
|
||||
ssl.setTrustCertificatePrivateKey("classpath:test-key.pem");
|
||||
SslStoreProvider storeProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), KeyStore.getDefaultType(), "test-alias");
|
||||
assertStoreContainsCertAndKey(storeProvider.getTrustStore(), KeyStore.getDefaultType(), "test-alias");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWithStoreTypeReturnsStoreProvider() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(true);
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
ssl.setTrustCertificate("classpath:test-cert.pem");
|
||||
ssl.setTrustCertificatePrivateKey("classpath:test-key.pem");
|
||||
SslStoreProvider storeProvider = CertificateFileSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), "PKCS12", "spring-boot-web");
|
||||
assertStoreContainsCertAndKey(storeProvider.getTrustStore(), "PKCS12", "spring-boot-web");
|
||||
}
|
||||
|
||||
private void assertStoreContainsCertAndKey(KeyStore keyStore, String keyStoreType, String keyAlias)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
||||
assertThat(keyStore).isNotNull();
|
||||
assertThat(keyStore.getType()).isEqualTo(keyStoreType);
|
||||
assertThat(keyStore.containsAlias(keyAlias)).isTrue();
|
||||
assertThat(keyStore.getCertificate(keyAlias)).isNotNull();
|
||||
assertThat(keyStore.getKey(keyAlias, new char[] {})).isNotNull();
|
||||
}
|
||||
|
||||
private void assertStoreContainsCert(KeyStore keyStore, String keyStoreType, String keyAlias)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
||||
assertThat(keyStore).isNotNull();
|
||||
assertThat(keyStore.getType()).isEqualTo(keyStoreType);
|
||||
assertThat(keyStore.containsAlias(keyAlias)).isTrue();
|
||||
assertThat(keyStore.getCertificate(keyAlias)).isNotNull();
|
||||
assertThat(keyStore.getKey(keyAlias, new char[] {})).isNull();
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link CertificateParser}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class CertificateParserTests {
|
||||
|
||||
@Test
|
||||
void parseCertificate() {
|
||||
X509Certificate[] certificates = CertificateParser.parse("classpath:test-cert.pem");
|
||||
assertThat(certificates).isNotNull();
|
||||
assertThat(certificates).hasSize(1);
|
||||
assertThat(certificates[0].getType()).isEqualTo("X.509");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseCertificateChain() {
|
||||
X509Certificate[] certificates = CertificateParser.parse("classpath:test-cert-chain.pem");
|
||||
assertThat(certificates).isNotNull();
|
||||
assertThat(certificates).hasSize(2);
|
||||
assertThat(certificates[0].getType()).isEqualTo("X.509");
|
||||
assertThat(certificates[1].getType()).isEqualTo("X.509");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseWithInvalidPathWillThrowException() {
|
||||
String path = "file:///bad/path/cert.pem";
|
||||
assertThatIllegalStateException().isThrownBy(() -> CertificateParser.parse("file:///bad/path/cert.pem"))
|
||||
.withMessageContaining(path);
|
||||
}
|
||||
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.embedded.test.MockPkcs11Security;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaKeyStoreSslStoreProvider}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@MockPkcs11Security
|
||||
class JavaKeyStoreSslStoreProviderTests {
|
||||
|
||||
@Test
|
||||
void fromSslWhenNullReturnsNull() {
|
||||
assertThat(JavaKeyStoreSslStoreProvider.from(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromSslWhenDisabledReturnsNull() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(false);
|
||||
assertThat(JavaKeyStoreSslStoreProvider.from(ssl)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyStoreWithNoLocationThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThatIllegalStateException().isThrownBy(storeProvider::getKeyStore)
|
||||
.withMessageContaining("KeyStore location must not be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyStoreWithTypePKCS11AndLocationThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("test.jks");
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThatIllegalStateException().isThrownBy(storeProvider::getKeyStore)
|
||||
.withMessageContaining("KeyStore location is 'test.jks', but must be empty or null for PKCS11 key stores");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyStoreWithLocationReturnsKeyStore() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("classpath:test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), "JKS", "test-alias", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTrustStoreWithLocationsReturnsTrustStore() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setTrustStore("classpath:test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getTrustStore(), "JKS", "test-alias", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyStoreWithTypeUsesType() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("classpath:test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getKeyStore(), "PKCS12", "test-alias", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTrustStoreWithTypeUsesType() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setTrustStore("classpath:test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThat(storeProvider).isNotNull();
|
||||
assertStoreContainsCertAndKey(storeProvider.getTrustStore(), "PKCS12", "test-alias", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getKeyStoreWithProviderUsesProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("classpath:test.jks");
|
||||
ssl.setKeyStoreProvider("com.example.KeyStoreProvider");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThatExceptionOfType(NoSuchProviderException.class).isThrownBy(storeProvider::getKeyStore)
|
||||
.withMessageContaining("com.example.KeyStoreProvider");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTrustStoreWithProviderUsesProvider() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setTrustStore("classpath:test.jks");
|
||||
ssl.setTrustStoreProvider("com.example.TrustStoreProvider");
|
||||
SslStoreProvider storeProvider = JavaKeyStoreSslStoreProvider.from(ssl);
|
||||
assertThatExceptionOfType(NoSuchProviderException.class).isThrownBy(storeProvider::getTrustStore)
|
||||
.withMessageContaining("com.example.TrustStoreProvider");
|
||||
}
|
||||
|
||||
private void assertStoreContainsCertAndKey(KeyStore keyStore, String keyStoreType, String keyAlias,
|
||||
String keyPassword) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
||||
assertThat(keyStore).isNotNull();
|
||||
assertThat(keyStore.getType()).isEqualTo(keyStoreType);
|
||||
assertThat(keyStore.containsAlias(keyAlias)).isTrue();
|
||||
assertThat(keyStore.getCertificate(keyAlias)).isNotNull();
|
||||
assertThat(keyStore.getKey(keyAlias, keyPassword.toCharArray())).isNotNull();
|
||||
}
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link PrivateKeyParser}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class PrivateKeyParserTests {
|
||||
|
||||
@Test
|
||||
void parsePkcs8KeyFile() {
|
||||
PrivateKey privateKey = PrivateKeyParser.parse("classpath:test-key.pem");
|
||||
assertThat(privateKey).isNotNull();
|
||||
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
|
||||
assertThat(privateKey.getAlgorithm()).isEqualTo("RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsePkcs8KeyFileWithEcdsa() {
|
||||
PrivateKey privateKey = PrivateKeyParser.parse("classpath:test-ec-key.pem");
|
||||
assertThat(privateKey).isNotNull();
|
||||
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
|
||||
assertThat(privateKey.getAlgorithm()).isEqualTo("EC");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseWithNonKeyFileWillThrowException() {
|
||||
String path = "classpath:test-banner.txt";
|
||||
assertThatIllegalStateException().isThrownBy(() -> PrivateKeyParser.parse("file://" + path))
|
||||
.withMessageContaining(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseWithInvalidPathWillThrowException() {
|
||||
String path = "file:///bad/path/key.pem";
|
||||
assertThatIllegalStateException().isThrownBy(() -> PrivateKeyParser.parse(path)).withMessageContaining(path);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundleKey;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.boot.ssl.SslStoreBundle;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebServerSslBundle}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class WebServerSslBundleTests {
|
||||
|
||||
@Test
|
||||
void whenSslDisabledThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setEnabled(false);
|
||||
assertThatIllegalStateException().isThrownBy(() -> WebServerSslBundle.get(ssl))
|
||||
.withMessage("SSL is not enabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenFromJksProperties() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("classpath:test.p12");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
ssl.setTrustStore("classpath:test.p12");
|
||||
ssl.setTrustStorePassword("secret");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyAlias("alias");
|
||||
ssl.setClientAuth(Ssl.ClientAuth.NONE);
|
||||
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
|
||||
ssl.setProtocol("TestProtocol");
|
||||
SslBundle bundle = WebServerSslBundle.get(ssl);
|
||||
assertThat(bundle).isNotNull();
|
||||
assertThat(bundle.getProtocol()).isEqualTo("TestProtocol");
|
||||
SslBundleKey key = bundle.getKey();
|
||||
assertThat(key.getPassword()).isEqualTo("password");
|
||||
assertThat(key.getAlias()).isEqualTo("alias");
|
||||
SslStoreBundle stores = bundle.getStores();
|
||||
assertThat(stores.getKeyStorePassword()).isEqualTo("secret");
|
||||
assertThat(stores.getKeyStore()).isNotNull();
|
||||
assertThat(stores.getTrustStore()).isNotNull();
|
||||
SslOptions options = bundle.getOptions();
|
||||
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
|
||||
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenFromJksPropertiesWithPkcs11StoreType() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setKeyStoreType("PKCS11");
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setClientAuth(Ssl.ClientAuth.NONE);
|
||||
SslBundle bundle = WebServerSslBundle.get(ssl);
|
||||
assertThat(bundle).isNotNull();
|
||||
assertThat(bundle.getStores().getKeyStorePassword()).isEqualTo("secret");
|
||||
assertThat(bundle.getKey().getPassword()).isEqualTo("password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenFromPemProperties() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setCertificate("classpath:test-cert.pem");
|
||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||
ssl.setTrustCertificate("classpath:test-cert-chain.pem");
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setClientAuth(Ssl.ClientAuth.NONE);
|
||||
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
|
||||
ssl.setProtocol("TLSv1.1");
|
||||
SslBundle bundle = WebServerSslBundle.get(ssl);
|
||||
assertThat(bundle).isNotNull();
|
||||
SslBundleKey key = bundle.getKey();
|
||||
assertThat(key.getAlias()).isNull();
|
||||
assertThat(key.getPassword()).isEqualTo("password");
|
||||
SslStoreBundle stores = bundle.getStores();
|
||||
assertThat(stores.getKeyStorePassword()).isNull();
|
||||
assertThat(stores.getKeyStore()).isNotNull();
|
||||
assertThat(stores.getTrustStore()).isNotNull();
|
||||
SslOptions options = bundle.getOptions();
|
||||
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
|
||||
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.1.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void whenFromCustomSslStoreProvider() throws Exception {
|
||||
SslStoreProvider sslStoreProvider = mock(SslStoreProvider.class);
|
||||
KeyStore keyStore = loadStore();
|
||||
given(sslStoreProvider.getKeyStore()).willReturn(keyStore);
|
||||
given(sslStoreProvider.getTrustStore()).willReturn(keyStore);
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStoreType("PKCS12");
|
||||
ssl.setTrustStoreType("PKCS12");
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setClientAuth(Ssl.ClientAuth.NONE);
|
||||
ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" });
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
|
||||
ssl.setProtocol("TLSv1.1");
|
||||
SslBundle bundle = WebServerSslBundle.get(ssl, null, sslStoreProvider);
|
||||
assertThat(bundle).isNotNull();
|
||||
SslBundleKey key = bundle.getKey();
|
||||
assertThat(key.getPassword()).isEqualTo("password");
|
||||
assertThat(key.getAlias()).isNull();
|
||||
SslStoreBundle stores = bundle.getStores();
|
||||
assertThat(stores.getKeyStore()).isNotNull();
|
||||
assertThat(stores.getTrustStore()).isNotNull();
|
||||
SslOptions options = bundle.getOptions();
|
||||
assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE");
|
||||
assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenMissingPropertiesThrowsException() {
|
||||
Ssl ssl = new Ssl();
|
||||
assertThatIllegalStateException().isThrownBy(() -> WebServerSslBundle.get(ssl))
|
||||
.withMessageContaining("SSL is enabled but no trust material is configured");
|
||||
}
|
||||
|
||||
private KeyStore loadStore() throws Exception {
|
||||
Resource resource = new ClassPathResource("test.p12");
|
||||
try (InputStream stream = resource.getInputStream()) {
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
keyStore.load(stream, "secret".toCharArray());
|
||||
return keyStore;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue