Securing Spring Boot 3 Applications With SSL Bundles – 使用 SSL 捆绑程序保障 Spring Boot 3 应用程序的安全

最后修改: 2023年 9月 21日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.导言

Managing secure communications in Spring Boot applications often involves dealing with complex configurations. The challenges usually start with handling trust material, such as certificates and private keys, that come in various formats like JKS, PKCS #12, or PEM. Each of these formats has its own set of requirements for how it should be processed.

在 Spring Boot 应用程序中管理安全通信通常需要处理复杂的配置。这些挑战通常从处理信任材料(如证书和私钥)开始,信任材料有各种格式,如 JKS、PKCS #12 或 PEM。每种格式都有自己的处理要求。

Fortunately, Spring Boot 3.1 introduces SSL Bundles, a feature designed to simplify these complexities. In this tutorial, we’ll explore what SSL Bundles are and how they can streamline SSL configuration tasks for Spring Boot applications.

幸运的是,Spring Boot 3.1 引入了 SSL 捆绑程序,该功能旨在简化这些复杂性。在本教程中,我们将探讨 SSL 捆绑是什么,以及它们如何简化 Spring Boot 应用程序的SSL 配置任务。

2. Spring Boot SSL Bundles

2. Spring Boot SSL 捆绑程序

Usually, once we’ve obtained the trust material, we need to turn it into Java objects that the application can work with. This often means dealing with classes like java.security.KeyStore for storing key material, javax.net.ssl.KeyManager for managing this key material, and javax.net.ssl.SSLContext for creating secure socket connections.

通常,在获得信任材料后,我们需要将其转化为应用程序可以使用的 Java 对象。这通常意味着要处理用于存储密钥材料的 java.security.KeyStore 类、用于管理这些密钥材料的 javax.net.ssl.KeyManager 类和用于创建安全套接字连接的 javax.net.ssl.SSLContext 类。

Each of these classes requires another layer of understanding and configuration, making the process tedious and error-prone. Various Spring Boot components might also necessitate delving into different layers of abstraction to apply these settings, adding another level of difficulty to the task.

这些类中的每一个都需要另一层的理解和配置,从而使整个过程变得乏味且容易出错。各种Spring Boot 组件可能还需要深入到不同的抽象层才能应用这些设置,这又给任务增加了难度。

An SSL Bundle encapsulates all the trust material and configuration settings, such as keystores, certificates, and private keys, into a single, easily manageable unit. Once an SSL Bundle is configured, it can be applied to one or more network connections, whether they are incoming or outgoing.

SSL捆绑包将所有信任材料和配置设置(如密钥存储库、证书和私钥)封装到一个易于管理的单元中。

Configuration properties for SSL Bundles reside under the spring.ssl.bundle prefix in the application.yaml or application.properties configuration files.

SSL 捆绑程序的配置属性位于 application.yamlapplication.properties 配置文件中的 spring.ssl.bundle 前缀下。

Let’s start with JKS Bundles. We use spring.ssl.bundle.jks to configure bundles that use Java Keystore files:

让我们从 JKS 捆绑程序开始。我们使用 spring.ssl.bundle.jks来配置使用 Java Keystore 文件的捆绑包:
</span

spring:
  ssl:
    bundle:
      jks:
        server:
          key:
            alias: "server"
          keystore:
            location: "classpath:server.p12"
            password: "secret"
            type: "PKCS12"

In the case of PEM Bundles, we use spring.ssl.bundle.pem to configure bundles using PEM-encoded text files:

在 PEM 捆绑的情况下,我们使用 spring.ssl.bundle.pem使用 PEM 编码文本文件配置捆绑:
</span

spring:
  ssl:
    bundle:
      pem:
        client:
          truststore:
            certificate: "classpath:client.crt"

Once these bundles are configured, they can be applied across microservices—whether it’s an Inventory Service needing to securely access a database, a User Authentication Service requiring secure API calls, or a Payment Processing Service securely communicating with a payment gateway.

一旦配置了这些捆绑包,它们就可以应用于各种微服务–无论是需要安全访问数据库的库存服务、需要安全调用 API 的用户身份验证服务,还是与支付网关安全通信的支付处理服务。

Spring Boot automates the creation of Java objects like KeyStore, KeyManager, and SSLContext based on SSL Bundle configurations. This removes the need to manually create and manage these objects, making the process more straightforward and less prone to errors.

Spring Boot 可根据 SSL Bundle 配置自动创建 KeyStoreKeyManagerSSLContext 等 Java 对象。这消除了手动创建和管理这些对象的需要,使创建过程更加简单,不易出错。

3. Securing RestTemplate With SSL Bundles

3.使用 SSL 捆绑程序确保 RestTemplate 的安全

Let’s start with leveraging SSL Bundles while using the RestTemplate bean. For that, we’ll use a sample Spring Boot app, but first, we need to generate keys that will be used as an SSL Bundle.

让我们从使用 RestTemplate Bean 时利用 SSL 捆绑开始。为此,我们将使用 Spring Boot 示例应用程序,但首先,我们需要生成将用作 SSL 捆绑程序的密钥。

We’ll use the openssl binary (that’s usually installed along with git) to generate keys by executing the following command from the project root:

我们将使用 openssl二进制文件(通常与 git 一起安装)生成密钥,方法是从项目根目录执行以下命令:

$ openssl req -x509 -newkey rsa:4096 -keyout src/main/resources/key.pem -out src/main/resources/cert.pem -days 365 -passout pass:FooBar

Now, let’s convert this key to the PKCS12 format:

现在,让我们将此密钥转换为 PKCS12 格式:</span

$ openssl pkcs12 -export -in src/main/resources/cert.pem -inkey src/main/resources/key.pem -out src/main/resources/keystore.p12 -name secure-service -passin pass:FooBar -passout pass:FooBar

As a result, we have everything for configuring SSL bundles; let’s define a bundle named “secure-service”  in the application.yml file:

因此,我们拥有了配置 SSL 捆绑程序的一切;让我们在 application.yml 文件中定义一个名为 “secure-service” 的捆绑程序:

spring:
 ssl:
   bundle:
     jks:
       secure-service:
         key:
           alias: "secure-service"
         keystore:
           location: "classpath:keystore.p12"
           password: "FooBar"
           type: "PKCS12"

Next, we can set our bundle on RestTemplate by calling the setSslBundle() method:

接下来,我们可以通过调用 setSslBundle() 方法在 RestTemplate 上设置捆绑包:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
    return restTemplateBuilder.setSslBundle(sslBundles.getBundle("secure-service")).build();
}

Finally, we can use the configured RestTemplate bean to call an API:

最后,我们可以使用配置的 RestTemplate Bean 调用 API:

@Service
public class SecureServiceRestApi {
    private final RestTemplate restTemplate;

    @Autowired
    public SecureServiceRestApi(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String fetchData(String dataId) {
        ResponseEntity<String> response = restTemplate.exchange(
          "https://secure-service.com/api/data/{id}",
          HttpMethod.GET,
          null,
          String.class,
          dataId
        );
        return response.getBody();
    }
}

The SSL Bundle in our Spring Boot application serves to validate the certificate of the secure-service, ensuring an encrypted and secure communication channel. However, this doesn’t limit us from using client certificates for authentication on the API side. We’ll see later how to obtain SSLContext to configure custom clients.

Spring Boot 应用程序中的 SSL 捆绑程序用于验证 secure-service 的证书,确保加密和安全的通信通道。但是,这并不限制我们在 API 端使用客户端证书进行身份验证。我们稍后将了解如何获取SSLContext来配置自定义客户端。

4. Leveraging Spring Boot’s Auto-Configured SSLBundles

4.利用 Spring Boot 的自动配置 SSLBundles

Before Spring Boot’s SSL Bundles, developers used to work with the classic Java classes that underpin SSL configurations:

在 Spring Boot 的 SSL 捆绑包之前,开发人员使用的是支撑 SSL 配置的 经典 Java 类

  • java.security.KeyStore: These instances are used as keystores and truststores, effectively serving as secure repositories of cryptographic keys and certificates.
  • javax.net.ssl.KeyManager and javax.net.ssl.TrustManager: These instances manage the keys and trust decisions during SSL communications, respectively.
  • javax.net.ssl.SSLContext: These instances act as a factory for SSLEngine and SSLSocket objects, orchestrating how SSL configurations are implemented at runtime.

Spring Boot 3.1 introduces a structured abstraction layer divided into Java interfaces:

Spring Boot 3.1 引入了一个结构化的抽象层,分为多个 Java 接口

  • SslStoreBundle: Offers a gateway to KeyStore objects containing cryptographic keys and trusted certificates.
  • SslManagerBundle: Coordinates and provides methods to manage KeyManager and TrustManager objects.
  • SslBundle: Serves as a one-stop shop, aggregating all these functionalities into a unified interaction model with the SSL ecosystem.

Subsequently, Spring Boot auto-configures an SslBundles bean. As a result, we can conveniently inject SslBundle instances into any Spring Bean. This is exceptionally useful for configuring secure communications for legacy codebase and custom REST clients.

随后,Spring Boot 将自动配置 SlBundles Bean。因此,我们可以方便地将 SslBundle 实例注入到任何 Spring Bean 中。这对于为遗留代码库和自定义 REST 客户端配置安全通信非常有用。

For example, let’s consider a custom SSLContext is needed for a custom secure HttpClient:

例如,让我们考虑一下自定义安全 HttpClient 需要自定义 SSLContext 的情况:

@Component
public class SecureRestTemplateConfig {
    private final SSLContext sslContext;

    @Autowired
    public SecureRestTemplateConfig(SslBundles sslBundles) throws NoSuchSslBundleException {
        SslBundle sslBundle = sslBundles.getBundle("secure-service");
        this.sslContext = sslBundle.createSslContext();
    }

    @Bean
    public RestTemplate secureRestTemplate() {
        SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(this.sslContext).build();
        HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
        HttpClient httpClient = HttpClients.custom().setConnectionManager(cm).evictExpiredConnections().build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(factory);
    }
}

In the code above, we inject the SslBundles instance into the Autowired constructor. Actually, SslBundles provides us access to all configured SSL Bundles. Therefore, we retrieve the secure-service bundle and create the context. Later, we use the SSLContext instance to create a custom HttpClient and apply it to create a RestTemplate bean.

在上面的代码中,我们将 SslBundles 实例注入到 Autowired 构造函数中。实际上,SlBundles 使我们能够访问所有已配置的 SSL 捆绑程序。因此,我们检索 secure-service 捆绑程序并创建上下文。随后,我们使用 SSLContext 实例创建自定义 HttpClient 并将其应用于创建 RestTemplate Bean。

5. Using SSL Bundles With Data Services

5.使用 SSL 数据服务捆绑包

Different data services have varying degrees of SSL configuration options, creating complexities during the configuration process.

不同的数据服务有不同程度的 SSL 配置选项,这就造成了配置过程的复杂性。

SSL Bundles introduces a more uniform approach to SSL configurations across a wide range of data services:

SSL捆绑包为各种数据服务的SSL配置引入了一种更加统一的方法

  • Cassandra: spring.cassandra.ssl
  • Couchbase: spring.couchbase.env.ssl
  • Elasticsearch: spring.elasticsearch.restclient.ssl
  • MongoDB: spring.data.mongodb.ssl
  • Redis: spring.data.redis.ssl

Now, most of these services support a *.ssl.enabled property. This property activates SSL support in the client library, leveraging the trust material found in the Java runtime’s cacerts.

现在,大多数这些服务都支持 *.ssl.enabled 属性。该属性将激活客户端库中的 SSL 支持,并利用 Java 运行时 cacerts 中的信任材料。

Additionally, the *.ssl.bundle property allows us to apply a named SSL bundle to customize the trust material, thereby achieving uniformity and reusability across multiple service connections.

此外,*.ssl.bundle 属性允许我们应用已命名的 SSL 捆绑程序来定制信任材料,从而在多个服务连接中实现统一性和可重用性。

For this example, let’s assume that there’s an SSL bundle named mongodb-ssl-bundle. This bundle contains the necessary trust material to secure connections to a MongoDB instance.

在本示例中,我们假设有一个名为 mongodb-ssl-bundle 的 SSL 捆绑程序。该捆绑包包含必要的信任材料,可确保与 MongoDB 实例的连接安全。

Let’s define the application.yml file:

让我们定义 application.yml 文件:

spring:
  data:
    mongodb:
      ssl:
        enabled: true
        bundle: mongodb-ssl-bundle

By simply adding these properties, the MongoDB client library in the Spring Boot application automatically uses the SSL context and trust material specified in mongodb-ssl-bundle.

只需添加这些属性,Spring Boot 应用程序中的 MongoDB 客户端库就会自动使用 mongodb-ssl-bundle 中指定的 SSL 上下文和信任材料。

6. Using SSL Bundles With Embedded Servers

6.与嵌入式服务器一起使用 SSL 捆绑程序

Managing SSL configurations for embedded web servers in Spring Boot can be also simplified by using SSL Bundles.

使用 SSL Bundles 还可以简化 Spring Boot 中嵌入式 Web 服务器 SSL 配置的管理。

Traditionally, the server.ssl.*  properties have been used for setting each individual SSL configuration. With SSL Bundles, configurations can be grouped together and then reused across multiple connections, reducing the chance of mistakes and simplifying overall management.

传统上,server.ssl.* 属性用于设置每个单独的 SSL 配置。使用 SSL 捆绑包,可以将配置分组,然后在多个连接中重复使用,从而减少出错的机会并简化整体管理

Firstly, let’s explore the traditional, individual property approach:

首先,让我们探讨一下传统的个人财产方法:

server:
  ssl:
    key-alias: "server"
    key-password: "keysecret"
    key-store: "classpath:server.p12"
    key-store-password: "storesecret"
    client-auth: NEED

On the other hand, the SSL Bundle approach allows for the same configurations to be encapsulated:

另一方面,SSL Bundle 方法允许对相同的配置进行封装:

spring:
  ssl:
    bundle:
      jks:
        web-server:
          key:
            alias: "server"
            password: "keysecret"
          keystore:
            location: "classpath:server.p12"
            password: "storesecret"

Now, we can use the defined bundle for securing our web server:

现在,我们可以使用已定义的捆绑包来确保网络服务器的安全:

server:
  ssl:
    bundle: "web-server"
    client-auth: NEED

While both approaches secure an embedded web server, the SSL Bundle method is more efficient for configuration management. This feature is also available for other configurations like management.server.ssl and spring.rsocket.server.ssl.

虽然这两种方法都能确保嵌入式网络服务器的安全,但 SSL 捆绑方法在配置管理方面更有效。该功能也适用于其他配置,如 management.server.sslspring.rsocket.server.ssl

7. Conclusion

7.结论

In this article, we explored the new SSL Bundles feature in Spring Boot that can simplify and unify the process of configuring trust materials.

在本文中,我们探讨了 Spring Boot 中新的 SSL Bundles 功能,它可以简化和统一信任材料的配置过程。

In contrast to traditional server.ssl.* properties, SSL Bundles offer a structured way to manage both keystores and truststores. This is particularly beneficial for reducing misconfiguration risks and increasing the efficiency of managing SSL across multiple services.

与传统的 server.ssl.* 属性相比,SSL 捆绑程序提供了一种结构化的方式来管理密钥存储和信任存储。这尤其有利于降低错误配置风险,提高跨多个服务管理 SSL 的效率。

Furthermore, SSL Bundles lend themselves well to centralized management, allowing the same bundle to be reused across different parts of the application.

此外,SSL 捆绑程序非常适合集中管理,允许在应用程序的不同部分重复使用相同的捆绑程序

By incorporating SSL bundles, developers can not only simplify the configuration process but also elevate the security posture of embedded web servers in Spring Boot applications.

通过集成 SSL 捆绑包,开发人员不仅可以简化配置过程,还能提升 Spring Boot 应用程序中嵌入式网络服务器的安全等级。

As always, the entire code examples can be found over on GitHub.

一如既往,您可以在 GitHub 上找到整个代码示例