Skip to content

Commit 6fcbf14

Browse files
committed
Add factory methods to create an SslManagerBundle from trust managers
Add two methods to create an SslManagerBundle from a given TrustManagerFactory or from a single or multiple TrustManagers. Both those methods use the default KeyManagerFactory. Closes gh-43064
1 parent f24ba99 commit 6fcbf14

File tree

4 files changed

+203
-2
lines changed

4 files changed

+203
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.ssl;
18+
19+
import java.security.KeyStore;
20+
import java.security.Provider;
21+
22+
import javax.net.ssl.ManagerFactoryParameters;
23+
import javax.net.ssl.TrustManager;
24+
import javax.net.ssl.TrustManagerFactory;
25+
import javax.net.ssl.TrustManagerFactorySpi;
26+
27+
import org.springframework.boot.SpringBootVersion;
28+
29+
/**
30+
* {@link TrustManagerFactory} which uses a fixed set of {@link TrustManager
31+
* TrustManagers}.
32+
*
33+
* @author Moritz Halbritter
34+
*/
35+
final class FixedTrustManagerFactory extends TrustManagerFactory {
36+
37+
private static final Provider PROVIDER = new Provider("FixedTrustManagerFactory", SpringBootVersion.getVersion(),
38+
"") {
39+
};
40+
41+
private FixedTrustManagerFactory(FixedTrustManagersSpi spi, String algorithm) {
42+
super(spi, PROVIDER, algorithm);
43+
}
44+
45+
static FixedTrustManagerFactory of(TrustManagerFactory trustManagerFactory, TrustManager... trustManagers) {
46+
return new FixedTrustManagerFactory(new FixedTrustManagersSpi(trustManagers),
47+
trustManagerFactory.getAlgorithm());
48+
}
49+
50+
private static final class FixedTrustManagersSpi extends TrustManagerFactorySpi {
51+
52+
private final TrustManager[] trustManagers;
53+
54+
private FixedTrustManagersSpi(TrustManager[] trustManagers) {
55+
this.trustManagers = trustManagers;
56+
}
57+
58+
@Override
59+
protected void engineInit(KeyStore ks) {
60+
}
61+
62+
@Override
63+
protected void engineInit(ManagerFactoryParameters spec) {
64+
}
65+
66+
@Override
67+
protected TrustManager[] engineGetTrustManagers() {
68+
return this.trustManagers;
69+
}
70+
71+
}
72+
73+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ssl/SslManagerBundle.java

+61
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
package org.springframework.boot.ssl;
1818

19+
import java.security.KeyStore;
20+
import java.security.KeyStoreException;
21+
import java.security.NoSuchAlgorithmException;
22+
import java.security.UnrecoverableKeyException;
23+
1924
import javax.net.ssl.KeyManager;
2025
import javax.net.ssl.KeyManagerFactory;
2126
import javax.net.ssl.SSLContext;
@@ -30,6 +35,7 @@
3035
* {@link SslStoreBundle}.
3136
*
3237
* @author Scott Frederick
38+
* @author Moritz Halbritter
3339
* @since 3.1.0
3440
* @see SslStoreBundle
3541
* @see SslBundle#getManagers()
@@ -118,4 +124,59 @@ static SslManagerBundle from(SslStoreBundle storeBundle, SslBundleKey key) {
118124
return new DefaultSslManagerBundle(storeBundle, key);
119125
}
120126

127+
/**
128+
* Factory method to create a new {@link SslManagerBundle} using the given
129+
* {@link TrustManagerFactory} and the default {@link KeyManagerFactory}.
130+
* @param trustManagerFactory the trust manager factory
131+
* @return a new {@link SslManagerBundle} instance
132+
* @since 3.5.0
133+
*/
134+
static SslManagerBundle from(TrustManagerFactory trustManagerFactory) {
135+
Assert.notNull(trustManagerFactory, "TrustManagerFactory must not be null");
136+
KeyManagerFactory defaultKeyManagerFactory = createDefaultKeyManagerFactory();
137+
return of(defaultKeyManagerFactory, trustManagerFactory);
138+
}
139+
140+
/**
141+
* Factory method to create a new {@link SslManagerBundle} using the given
142+
* {@link TrustManager TrustManagers} and the default {@link KeyManagerFactory}.
143+
* @param trustManagers the trust managers to use
144+
* @return a new {@link SslManagerBundle} instance
145+
* @since 3.5.0
146+
*/
147+
static SslManagerBundle from(TrustManager... trustManagers) {
148+
Assert.notNull(trustManagers, "TrustManagers must not be null");
149+
KeyManagerFactory defaultKeyManagerFactory = createDefaultKeyManagerFactory();
150+
TrustManagerFactory defaultTrustManagerFactory = createDefaultTrustManagerFactory();
151+
return of(defaultKeyManagerFactory, FixedTrustManagerFactory.of(defaultTrustManagerFactory, trustManagers));
152+
}
153+
154+
private static TrustManagerFactory createDefaultTrustManagerFactory() {
155+
String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
156+
TrustManagerFactory trustManagerFactory;
157+
try {
158+
trustManagerFactory = TrustManagerFactory.getInstance(defaultAlgorithm);
159+
trustManagerFactory.init((KeyStore) null);
160+
}
161+
catch (NoSuchAlgorithmException | KeyStoreException ex) {
162+
throw new IllegalStateException(
163+
"Unable to create TrustManagerFactory for default '%s' algorithm".formatted(defaultAlgorithm), ex);
164+
}
165+
return trustManagerFactory;
166+
}
167+
168+
private static KeyManagerFactory createDefaultKeyManagerFactory() {
169+
String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
170+
KeyManagerFactory keyManagerFactory;
171+
try {
172+
keyManagerFactory = KeyManagerFactory.getInstance(defaultAlgorithm);
173+
keyManagerFactory.init(null, null);
174+
}
175+
catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException ex) {
176+
throw new IllegalStateException(
177+
"Unable to create KeyManagerFactory for default '%s' algorithm".formatted(defaultAlgorithm), ex);
178+
}
179+
return keyManagerFactory;
180+
}
181+
121182
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.ssl;
18+
19+
import java.security.NoSuchAlgorithmException;
20+
21+
import javax.net.ssl.TrustManager;
22+
import javax.net.ssl.TrustManagerFactory;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.Mockito.mock;
28+
29+
/**
30+
* Tests for {@link FixedTrustManagerFactory}.
31+
*
32+
* @author Moritz Halbritter
33+
*/
34+
class FixedTrustManagerFactoryTests {
35+
36+
@Test
37+
void shouldReturnTrustmanagers() throws Exception {
38+
TrustManager trustManager1 = mock(TrustManager.class);
39+
TrustManager trustManager2 = mock(TrustManager.class);
40+
FixedTrustManagerFactory factory = FixedTrustManagerFactory.of(getDefaultTrustManagerFactory(), trustManager1,
41+
trustManager2);
42+
assertThat(factory.getTrustManagers()).containsExactly(trustManager1, trustManager2);
43+
}
44+
45+
private static TrustManagerFactory getDefaultTrustManagerFactory() throws NoSuchAlgorithmException {
46+
return TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
47+
}
48+
49+
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ssl/SslManagerBundleTests.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import javax.net.ssl.KeyManagerFactory;
2020
import javax.net.ssl.SSLContext;
21+
import javax.net.ssl.TrustManager;
2122
import javax.net.ssl.TrustManagerFactory;
2223

2324
import org.junit.jupiter.api.Test;
@@ -31,12 +32,13 @@
3132
* Tests for {@link SslManagerBundle}.
3233
*
3334
* @author Phillip Webb
35+
* @author Moritz Halbritter
3436
*/
3537
class SslManagerBundleTests {
3638

37-
private KeyManagerFactory keyManagerFactory = mock(KeyManagerFactory.class);
39+
private final KeyManagerFactory keyManagerFactory = mock(KeyManagerFactory.class);
3840

39-
private TrustManagerFactory trustManagerFactory = mock(TrustManagerFactory.class);
41+
private final TrustManagerFactory trustManagerFactory = mock(TrustManagerFactory.class);
4042

4143
@Test
4244
void getKeyManagersDelegatesToFactory() {
@@ -85,4 +87,20 @@ void fromCreatesDefaultSslManagerBundle() {
8587
assertThat(bundle).isInstanceOf(DefaultSslManagerBundle.class);
8688
}
8789

90+
@Test
91+
void shouldReturnTrustManagerFactory() {
92+
SslManagerBundle bundle = SslManagerBundle.from(this.trustManagerFactory);
93+
assertThat(bundle.getKeyManagerFactory()).isNotNull();
94+
assertThat(bundle.getTrustManagerFactory()).isSameAs(this.trustManagerFactory);
95+
}
96+
97+
@Test
98+
void shouldReturnTrustManagers() {
99+
TrustManager trustManager1 = mock(TrustManager.class);
100+
TrustManager trustManager2 = mock(TrustManager.class);
101+
SslManagerBundle bundle = SslManagerBundle.from(trustManager1, trustManager2);
102+
assertThat(bundle.getKeyManagerFactory()).isNotNull();
103+
assertThat(bundle.getTrustManagerFactory().getTrustManagers()).containsExactly(trustManager1, trustManager2);
104+
}
105+
88106
}

0 commit comments

Comments
 (0)