Skip to content

Commit f5f7fc8

Browse files
Merge branch '2.7.x'
2 parents 9aff0a9 + d387b3f commit f5f7fc8

23 files changed

+910
-20
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,22 @@
243243
"name": "server.ssl.trust-store-type",
244244
"description": "Type of the trust store."
245245
},
246+
{
247+
"name": "server.ssl.certificate",
248+
"description": "Path to a PEM-encoded SSL certificate file."
249+
},
250+
{
251+
"name": "server.ssl.certificate-private-key",
252+
"description": "Path to a PEM-encoded private key file for the SSL certificate."
253+
},
254+
{
255+
"name": "server.ssl.trust-certificate",
256+
"description": "Path to a PEM-encoded SSL certificate authority file."
257+
},
258+
{
259+
"name": "server.ssl.trust-certificate-private-key",
260+
"description": "Path to a PEM-encoded private key file for the SSL certificate authority."
261+
},
246262
{
247263
"name": "server.tomcat.max-http-post-size",
248264
"type": "org.springframework.util.unit.DataSize",

spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/webserver.adoc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ You can configure this behavior by setting the configprop:server.compression.mim
183183
[[howto.webserver.configure-ssl]]
184184
=== Configure SSL
185185
SSL can be configured declaratively by setting the various `+server.ssl.*+` properties, typically in `application.properties` or `application.yml`.
186-
The following example shows setting SSL properties in `application.properties`:
186+
The following example shows setting SSL properties using a Java KeyStore file:
187187

188188
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
189189
----
@@ -195,6 +195,19 @@ The following example shows setting SSL properties in `application.properties`:
195195
key-password: "another-secret"
196196
----
197197

198+
The following example shows setting SSL properties using PEM-encoded certificate and private key files:
199+
200+
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
201+
----
202+
server:
203+
port: 8443
204+
ssl:
205+
certificate: "classpath:my-cert.crt"
206+
certificate-private-key: "classpath:my-cert.key"
207+
trust-certificate: "classpath:ca-cert.crt"
208+
key-store-password: "secret"
209+
----
210+
198211
See {spring-boot-module-code}/web/server/Ssl.java[`Ssl`] for details of all of the supported properties.
199212

200213
Using configuration such as the preceding example means the application no longer supports a plain HTTP connector at port 8080.

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/rsocket/netty/NettyRSocketServerFactory.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
import org.springframework.boot.rsocket.server.RSocketServer;
4040
import org.springframework.boot.rsocket.server.RSocketServerCustomizer;
4141
import org.springframework.boot.rsocket.server.RSocketServerFactory;
42+
import org.springframework.boot.web.server.CertificateFileSslStoreProvider;
4243
import org.springframework.boot.web.server.Ssl;
4344
import org.springframework.boot.web.server.SslStoreProvider;
4445
import org.springframework.http.client.reactive.ReactorResourceFactory;
@@ -51,6 +52,7 @@
5152
*
5253
* @author Brian Clozel
5354
* @author Chris Bono
55+
* @author Scott Frederick
5456
* @since 2.2.0
5557
*/
5658
public class NettyRSocketServerFactory implements RSocketServerFactory, ConfigurableRSocketServerFactory {
@@ -179,7 +181,7 @@ private ServerTransport<CloseableChannel> createWebSocketTransport() {
179181
@SuppressWarnings("deprecation")
180182
private HttpServer customizeSslConfiguration(HttpServer httpServer) {
181183
org.springframework.boot.web.embedded.netty.SslServerCustomizer sslServerCustomizer = new org.springframework.boot.web.embedded.netty.SslServerCustomizer(
182-
this.ssl, null, this.sslStoreProvider);
184+
this.ssl, null, getOrCreateSslStoreProvider());
183185
return sslServerCustomizer.apply(httpServer);
184186
}
185187

@@ -189,12 +191,20 @@ private ServerTransport<CloseableChannel> createTcpTransport() {
189191
tcpServer = tcpServer.runOn(this.resourceFactory.getLoopResources());
190192
}
191193
if (this.ssl != null && this.ssl.isEnabled()) {
192-
TcpSslServerCustomizer sslServerCustomizer = new TcpSslServerCustomizer(this.ssl, this.sslStoreProvider);
194+
TcpSslServerCustomizer sslServerCustomizer = new TcpSslServerCustomizer(this.ssl,
195+
getOrCreateSslStoreProvider());
193196
tcpServer = sslServerCustomizer.apply(tcpServer);
194197
}
195198
return TcpServerTransport.create(tcpServer.bindAddress(this::getListenAddress));
196199
}
197200

201+
private SslStoreProvider getOrCreateSslStoreProvider() {
202+
if (this.sslStoreProvider != null) {
203+
return this.sslStoreProvider;
204+
}
205+
return CertificateFileSslStoreProvider.from(this.ssl);
206+
}
207+
198208
private InetSocketAddress getListenAddress() {
199209
if (this.address != null) {
200210
return new InetSocketAddress(this.address.getHostAddress(), this.port);

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -236,7 +236,7 @@ private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) {
236236
}
237237

238238
private void customizeSsl(Server server, InetSocketAddress address) {
239-
new SslServerCustomizer(address, getSsl(), getSslStoreProvider(), getHttp2()).customize(server);
239+
new SslServerCustomizer(address, getSsl(), getOrCreateSslStoreProvider(), getHttp2()).customize(server);
240240
}
241241

242242
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) {
220220
}
221221

222222
private void customizeSsl(Server server, InetSocketAddress address) {
223-
new SslServerCustomizer(address, getSsl(), getSslStoreProvider(), getHttp2()).customize(server);
223+
new SslServerCustomizer(address, getSsl(), getOrCreateSslStoreProvider(), getHttp2()).customize(server);
224224
}
225225

226226
/**

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -179,7 +179,8 @@ private HttpServer createHttpServer() {
179179

180180
@SuppressWarnings("deprecation")
181181
private HttpServer customizeSslConfiguration(HttpServer httpServer) {
182-
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), getHttp2(), getSslStoreProvider());
182+
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), getHttp2(),
183+
getOrCreateSslStoreProvider());
183184
return sslServerCustomizer.apply(httpServer);
184185
}
185186

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ private void customizeProtocol(AbstractProtocol<?> protocol) {
221221
}
222222

223223
private void customizeSsl(Connector connector) {
224-
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
224+
new SslConnectorCustomizer(getSsl(), getOrCreateSslStoreProvider()).customize(connector);
225225
}
226226

227227
@Override

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ private void invokeProtocolHandlerCustomizers(ProtocolHandler protocolHandler) {
360360
}
361361

362362
private void customizeSsl(Connector connector) {
363-
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
363+
new SslConnectorCustomizer(getSsl(), getOrCreateSslStoreProvider()).customize(connector);
364364
}
365365

366366
/**

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServerFactoryDelegate.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -163,7 +163,8 @@ Builder createBuilder(AbstractConfigurableWebServerFactory factory) {
163163
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
164164
}
165165
if (ssl != null && ssl.isEnabled()) {
166-
new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getSslStoreProvider()).customize(builder);
166+
new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getOrCreateSslStoreProvider())
167+
.customize(builder);
167168
}
168169
else {
169170
builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
3636
* @author Ivan Sopov
3737
* @author Eddú Meléndez
3838
* @author Brian Clozel
39+
* @author Scott Frederick
3940
* @since 2.0.0
4041
*/
4142
public abstract class AbstractConfigurableWebServerFactory implements ConfigurableWebServerFactory {
@@ -179,6 +180,18 @@ public Shutdown getShutdown() {
179180
return this.shutdown;
180181
}
181182

183+
/**
184+
* Return the provided {@link SslStoreProvider} or create one using {@link Ssl}
185+
* properties.
186+
* @return the {@code SslStoreProvider}
187+
*/
188+
public final SslStoreProvider getOrCreateSslStoreProvider() {
189+
if (this.sslStoreProvider != null) {
190+
return this.sslStoreProvider;
191+
}
192+
return CertificateFileSslStoreProvider.from(this.ssl);
193+
}
194+
182195
/**
183196
* Return the absolute temp dir for given web server.
184197
* @param prefix server name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2012-2022 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.web.server;
18+
19+
import java.io.IOException;
20+
import java.security.GeneralSecurityException;
21+
import java.security.KeyStore;
22+
import java.security.KeyStoreException;
23+
import java.security.PrivateKey;
24+
import java.security.cert.X509Certificate;
25+
26+
/**
27+
* An {@link SslStoreProvider} that creates key and trust stores from certificate and
28+
* private key PEM files.
29+
*
30+
* @author Scott Frederick
31+
* @since 2.7.0
32+
*/
33+
public final class CertificateFileSslStoreProvider implements SslStoreProvider {
34+
35+
private static final char[] NO_PASSWORD = {};
36+
37+
private static final String DEFAULT_KEY_ALIAS = "spring-boot-web";
38+
39+
private final Ssl ssl;
40+
41+
private CertificateFileSslStoreProvider(Ssl ssl) {
42+
this.ssl = ssl;
43+
}
44+
45+
@Override
46+
public KeyStore getKeyStore() throws Exception {
47+
return createKeyStore(this.ssl.getCertificate(), this.ssl.getCertificatePrivateKey(),
48+
this.ssl.getKeyStorePassword(), this.ssl.getKeyStoreType(), this.ssl.getKeyAlias());
49+
}
50+
51+
@Override
52+
public KeyStore getTrustStore() throws Exception {
53+
if (this.ssl.getTrustCertificate() == null) {
54+
return null;
55+
}
56+
return createKeyStore(this.ssl.getTrustCertificate(), this.ssl.getTrustCertificatePrivateKey(),
57+
this.ssl.getTrustStorePassword(), this.ssl.getTrustStoreType(), this.ssl.getKeyAlias());
58+
}
59+
60+
/**
61+
* Create a new {@link KeyStore} populated with the certificate stored at the
62+
* specified file path and an optional private key.
63+
* @param certPath the path to the certificate authority file
64+
* @param keyPath the path to the private file
65+
* @param password the key store password
66+
* @param storeType the {@code KeyStore} type to create
67+
* @param keyAlias the alias to use when adding keys to the {@code KeyStore}
68+
* @return the {@code KeyStore}
69+
*/
70+
private KeyStore createKeyStore(String certPath, String keyPath, String password, String storeType,
71+
String keyAlias) {
72+
try {
73+
KeyStore keyStore = KeyStore.getInstance((storeType != null) ? storeType : KeyStore.getDefaultType());
74+
keyStore.load(null);
75+
X509Certificate[] certificates = CertificateParser.parse(certPath);
76+
PrivateKey privateKey = (keyPath != null) ? PrivateKeyParser.parse(keyPath) : null;
77+
try {
78+
addCertificates(keyStore, certificates, privateKey, password, keyAlias);
79+
}
80+
catch (KeyStoreException ex) {
81+
throw new IllegalStateException("Error adding certificates to KeyStore: " + ex.getMessage(), ex);
82+
}
83+
return keyStore;
84+
}
85+
catch (GeneralSecurityException | IOException ex) {
86+
throw new IllegalStateException("Error creating KeyStore: " + ex.getMessage(), ex);
87+
}
88+
}
89+
90+
private void addCertificates(KeyStore keyStore, X509Certificate[] certificates, PrivateKey privateKey,
91+
String password, String keyAlias) throws KeyStoreException {
92+
String alias = (keyAlias != null) ? keyAlias : DEFAULT_KEY_ALIAS;
93+
if (privateKey != null) {
94+
keyStore.setKeyEntry(alias, privateKey, ((password != null) ? password.toCharArray() : NO_PASSWORD),
95+
certificates);
96+
}
97+
else {
98+
for (int index = 0; index < certificates.length; index++) {
99+
keyStore.setCertificateEntry(alias + "-" + index, certificates[index]);
100+
}
101+
}
102+
}
103+
104+
/**
105+
* Create a {@link SslStoreProvider} if the appropriate SSL properties are configured.
106+
* @param ssl the SSL properties
107+
* @return a {@code SslStoreProvider} or {@code null}
108+
*/
109+
public static SslStoreProvider from(Ssl ssl) {
110+
if (ssl != null && ssl.isEnabled()) {
111+
if (ssl.getCertificate() != null && ssl.getCertificatePrivateKey() != null) {
112+
return new CertificateFileSslStoreProvider(ssl);
113+
}
114+
}
115+
return null;
116+
}
117+
118+
}

0 commit comments

Comments
 (0)