Add method to create an SslBundle which uses the system default managers

See gh-41137
This commit is contained in:
Moritz Halbritter 2025-02-11 10:07:32 +01:00
parent 631d07cfe1
commit d8b470a511
4 changed files with 69 additions and 6 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,16 @@
package org.springframework.boot.ssl; package org.springframework.boot.ssl;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.core.style.ToStringCreator; import org.springframework.core.style.ToStringCreator;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -27,6 +34,7 @@ import org.springframework.util.StringUtils;
* A bundle of trust material that can be used to establish an SSL connection. * A bundle of trust material that can be used to establish an SSL connection.
* *
* @author Scott Frederick * @author Scott Frederick
* @author Moritz Halbritter
* @since 3.1.0 * @since 3.1.0
*/ */
public interface SslBundle { public interface SslBundle {
@ -174,4 +182,40 @@ public interface SslBundle {
}; };
} }
/**
* Factory method to create a new {@link SslBundle} which uses the system defaults.
* @return a new {@link SslBundle} instance
* @since 3.5.0
*/
static SslBundle systemDefault() {
try {
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(null, null);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
SSLContext sslContext = SSLContext.getDefault();
return of(null, null, null, null, new SslManagerBundle() {
@Override
public KeyManagerFactory getKeyManagerFactory() {
return keyManagerFactory;
}
@Override
public TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactory;
}
@Override
public SSLContext createSslContext(String protocol) {
return sslContext;
}
});
}
catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException ex) {
throw new IllegalStateException("Could not initialize system default SslBundle: " + ex.getMessage(), ex);
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2024 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ public interface SslOptions {
/** /**
* {@link SslOptions} that returns {@code null} results. * {@link SslOptions} that returns {@code null} results.
*/ */
SslOptions NONE = of((Set<String>) null, (Set<String>) null); SslOptions NONE = of(null, (Set<String>) null);
/** /**
* Return if any SSL options have been specified. * Return if any SSL options have been specified.

View File

@ -33,6 +33,7 @@ import java.util.stream.Collectors;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/** /**
* PEM encoded content that can provide {@link X509Certificate certificates} and * PEM encoded content that can provide {@link X509Certificate certificates} and
@ -109,11 +110,11 @@ public final class PemContent {
* reference to the resource to load). * reference to the resource to load).
* @param content the content to load * @param content the content to load
* @param resourceLoader the resource loader used to load content * @param resourceLoader the resource loader used to load content
* @return a new {@link PemContent} instance * @return a new {@link PemContent} instance or {@code null}
* @throws IOException on IO error * @throws IOException on IO error
*/ */
static PemContent load(String content, ResourceLoader resourceLoader) throws IOException { static PemContent load(String content, ResourceLoader resourceLoader) throws IOException {
if (content == null) { if (!StringUtils.hasLength(content)) {
return null; return null;
} }
if (isPresentInText(content)) { if (isPresentInText(content)) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,10 @@
package org.springframework.boot.ssl; package org.springframework.boot.ssl;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -26,6 +30,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link SslBundle}. * Tests for {@link SslBundle}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Moritz Halbritter
*/ */
class SslBundleTests { class SslBundleTests {
@ -52,4 +57,17 @@ class SslBundleTests {
assertThat(bundle.getManagers()).isSameAs(managers); assertThat(bundle.getManagers()).isSameAs(managers);
} }
@Test
void shouldCreateSystemDefaultBundle() {
SslBundle sslBundle = SslBundle.systemDefault();
SSLContext sslContext = sslBundle.createSslContext();
assertThat(sslContext).isNotNull();
TrustManager[] trustManagers = sslBundle.getManagers().getTrustManagers();
assertThat(trustManagers).isNotEmpty();
TrustManager trustManager = trustManagers[0];
assertThat(trustManager).isInstanceOf(X509TrustManager.class);
X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
assertThat(x509TrustManager.getAcceptedIssuers()).isNotEmpty();
}
} }