1. 简介

在Spring Boot应用中管理安全通信通常涉及复杂的配置。挑战始于处理各种格式的信任材料(如JKS、PKCS #12或PEM格式的证书和私钥),每种格式都有特定的处理要求。

幸运的是,Spring Boot 3.1引入了SSL Bundles特性,旨在简化这些复杂性。本文将探讨SSL Bundles是什么,以及它们如何简化Spring Boot应用的SSL配置任务。

2. Spring Boot SSL Bundles

通常,获取信任材料后需要将其转换为应用可用的Java对象。这意味着要处理以下类:

  • java.security.KeyStore:存储密钥材料
  • javax.net.ssl.KeyManager:管理密钥材料
  • javax.net.ssl.SSLContext:创建安全套接字连接

每个类都需要额外的理解和配置层,使过程繁琐且容易出错。各种Spring Boot组件可能还需要深入不同抽象层来应用这些设置,进一步增加难度。

SSL Bundle将所有信任材料和配置设置(如密钥库、证书和私钥)封装到单个易管理的单元中。配置后,可应用于一个或多个网络连接(入站或出站)。

SSL Bundles的配置属性位于application.yamlapplication.propertiesspring.ssl.bundle前缀下。

2.1 JKS Bundles配置

使用spring.ssl.bundle.jks配置基于Java密钥库文件的Bundle:

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

2.2 PEM Bundles配置

使用spring.ssl.bundle.pem配置基于PEM编码文本文件的Bundle:

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

配置后,这些Bundle可在微服务间复用:

  • 库存服务安全访问数据库
  • 用户认证服务进行安全API调用
  • 支付处理服务与支付网关安全通信

Spring Boot会根据SSL Bundle配置自动创建KeyStoreKeyManagerSSLContext等Java对象,无需手动管理,使过程更简单且不易出错。

3. 使用SSL Bundles保护RestTemplate

让我们通过示例Spring Boot应用,了解如何在使用RestTemplate Bean时利用SSL Bundles。首先需要生成用作SSL Bundle的密钥。

使用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

将密钥转换为PKCS12格式:

$ 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

现在在application.yml中定义名为secure-service的Bundle:

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

通过调用setSslBundle()方法将Bundle应用到RestTemplate

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

使用配置好的RestTemplate调用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();
    }
}

Spring Boot应用中的SSL Bundle用于验证secure-service的证书,确保加密安全通信通道。但这并不限制我们在API端使用客户端证书进行身份验证,后续将展示如何获取SSLContext配置自定义客户端。

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

在SSL Bundles出现前,开发者需要处理支撑SSL配置的经典Java类:

  • java.security.KeyStore:用作密钥库和信任库,作为密钥和证书的安全存储库
  • javax.net.ssl.KeyManager/TrustManager:分别管理SSL通信中的密钥和信任决策
  • javax.net.ssl.SSLContext:作为SSLEngineSSLSocket对象的工厂,协调运行时SSL配置实现

Spring Boot 3.1引入了结构化的抽象层,分为以下Java接口

  • SslStoreBundle:提供访问包含密钥和受信任证书的KeyStore对象
  • SslManagerBundle:协调并提供管理KeyManagerTrustManager对象的方法
  • SslBundle:一站式服务,聚合所有功能到统一的SSL生态系统交互模型

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

例如,为自定义安全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);
    }
}

上述代码中,我们将SslBundles实例注入@Autowired构造函数。SslBundles提供对所有已配置SSL Bundles的访问,因此我们获取secure-service Bundle并创建上下文,然后使用SSLContext实例创建自定义HttpClient并应用于RestTemplate Bean。

5. 在数据服务中使用SSL Bundles

不同数据服务的SSL配置选项差异较大,导致配置过程复杂。

SSL Bundles为各种数据服务引入了更统一的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

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

此外,*.ssl.bundle属性允许应用命名的SSL Bundle来自定义信任材料,从而在多个服务连接间实现统一性和可重用性。

假设存在名为mongodb-ssl-bundle的SSL Bundle,包含安全连接到MongoDB实例所需的信任材料。在application.yml中定义:

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

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

6. 在嵌入式服务器中使用SSL Bundles

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

传统上使用server.ssl.*属性设置每个SSL配置项。而SSL Bundles允许将配置分组并在多个连接间复用,减少错误并简化整体管理

6.1 传统属性方式

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

6.2 SSL Bundle方式

首先封装配置:

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

然后使用Bundle保护Web服务器:

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

两种方式都能保护嵌入式Web服务器,但SSL Bundle方法在配置管理上更高效。该特性同样适用于management.server.sslspring.rsocket.server.ssl等其他配置。

7. 结论

本文探讨了Spring Boot中新的SSL Bundles特性,它能简化和统一信任材料的配置过程。

与传统server.ssl.*属性相比,SSL Bundles提供了结构化的密钥库和信任库管理方式,特别有助于减少配置错误风险,提高跨服务SSL管理效率。

此外,SSL Bundles非常适合集中管理,允许同一Bundle在应用不同部分复用

通过整合SSL Bundles,开发者不仅能简化配置过程,还能提升Spring Boot应用中嵌入式Web服务器的安全态势。

完整代码示例可在GitHub获取。


原始标题:Securing Spring Boot Applications With SSL Bundles | Baeldung