Spring Boot 3.2.0 中的 SSL 热重载

工程 | Moritz Halbritter | 2023 年 11 月 07 日 | ...

在 Spring Boot 3.2.0 中,我们添加了嵌入式 Web 服务器热重载 SSL 证书和密钥的功能。 这意味着您可以轮换您的 SSL 信任材料,而无需重新启动您的应用程序。 Tomcat 和 Netty 嵌入式 Web 服务器都支持热重载。

让我们看看它的实际效果!

首先,我们将使用 OpenSSL 创建我们的 SSL 私钥和匹配的证书

mkdir certs
cd certs
openssl req -x509 -subj "/CN=demo-cert-1" -keyout demo.key -out demo.crt -sha256 -days 365 -nodes -newkey rsa 

这将创建一个存储在 certs/demo.key 中的私钥,以及一个匹配的(自签名)证书,其通用名称为 certs/demo.crt 中的 "demo-cert-1"。

现在我们正在创建一个新的 Spring Boot 3.2.0 应用程序,使用 "Spring Web" 依赖项,默认情况下使用 Tomcat Web 服务器,使用我们最喜欢的网站 start.spring.io

在应用程序配置中,我们添加以下内容

spring.ssl.bundle.pem:
  demo:
    reload-on-update: true
    keystore:    
      certificate: "certs/demo.crt"
      private-key: "certs/demo.key"

这配置了一个名为 "demo" 的 SSL 捆绑包,以及我们生成的证书和私钥。 reload-on-update: true 配置指示 Spring Boot 在后台监视文件,并在文件更改时触发重新加载。

现在,我们配置 Web 服务器以使用该捆绑包并接受端口 8443 上的连接

server.ssl.bundle: "demo"
server.port: 8443

让我们也添加一个简单的控制器,它用纯文本 "Hello World" 进行响应

@RestController
class DemoController {
    @GetMapping(path = "/", produces = MediaType.TEXT_PLAIN_VALUE)
    String helloWorld() {
        return "Hello World";
    }
}

当我们启动应用程序时,我们在日志中看到类似这样的内容,这证实它已在端口 8443 上使用 HTTPS 启动。

INFO 82407 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8443 (https)

现在我们可以使用 curl 发出我们的第一个请求

$ curl --insecure https://localhost:8443/
Hello World%

万岁,它有效! 我们必须传递 --insecure,因为 SSL 证书是自签名且不受 curl 信任。

让我们将 curl 切换到 verbose 模式,以获取有关发生的 SSL 握手的一些信息

$ curl --verbose --insecure https://localhost:8443/
*   Trying 127.0.0.1:8443...
* Connected to localhost (127.0.0.1) port 8443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=demo-cert-1
*  start date: Nov  2 09:22:53 2023 GMT
*  expire date: Nov  1 09:22:53 2024 GMT
*  issuer: CN=demo-cert-1
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/8.0.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Thu, 02 Nov 2023 09:33:37 GMT
< 
* Connection #0 to host localhost left intact
Hello World

subject: CN=demo-cert-1 行中,您可以看到证书的通用名称是 "demo-cert-1",这在以后会很重要。

让我们通过使用 OpenSSL 生成新的私钥和证书来尝试热重载,覆盖旧文件

cd certs
openssl req -x509 -subj "/CN=demo-cert-2" -keyout demo.key -out demo.crt -sha256 -days 365 -nodes -newkey rsa

这次,我们的证书的通用名称为 "demo-cert-2"。

一段时间后,您会在日志中看到类似这样的内容

INFO 83162 --- [-bundle-watcher] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/home/xxx/.keystore] using alias [tomcat] with trust store [null]

这是一种复杂的说法,即 Tomcat 已重新加载私钥和证书。

我们现在可以使用 curl 验证是否使用了新证书

$ curl --verbose --insecure https://localhost:8443/
*   Trying 127.0.0.1:8443...
* Connected to localhost (127.0.0.1) port 8443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=demo-cert-2
*  start date: Nov  2 09:37:46 2023 GMT
*  expire date: Nov  1 09:37:46 2024 GMT
*  issuer: CN=demo-cert-2
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/8.0.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Thu, 02 Nov 2023 09:39:47 GMT
< 
* Connection #0 to host localhost left intact
Hello World

subject: CN=demo-cert-2 行验证使用了新证书。 万岁,大获成功!

概括一下:我们创建了一个 SSL 私钥和证书,然后配置 Spring Boot 以使用它并监视更改。 当私钥和证书更改时,Spring Boot 会重新加载它们,并且它们将在不重新启动应用程序的情况下使用。 这不是很酷吗?!

顺便说一句,如果您想知道现有连接会发生什么:它们将继续使用旧证书,但所有新连接将使用新证书。

我们希望此功能可以简化您的操作。 您可以通过测试 Spring Boot 3.2.0-RC2 来尝试一下。 如果您有增强的想法或发现错误,请随时在 我们的跟踪器上提出问题。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部