使用 SSL bundle 保护 Spring Boot 应用的安全

本文介绍了Spring Boot 3.1中引入的 SSL bundle如何简化了SSL的配置,以及如何应用到 REST 客户端, 数据服务连接和嵌入式服务器。

安全套接字层(SSL)和传输层安全(TLS)是确保分层或面向服务架构中系统间通信安全的关键组件。这种架构中的Spring Boot应用程序经常接受传入的网络连接或创建传出的连接,开发人员的任务是配置应用程序以在这种安全环境中工作。

如果你曾经使用过 Java security 和SSL API,你可能知道这不是一个特别有趣的任务。它往往涉及到多次到 stackoverflow.com 去复制和粘贴代码。有几个因素使使用SSL的工作变得很痛苦。

首先,你可能被提供信任(trust)材料,如证书和私钥,供生产使用。你可能需要为生产前的测试生成不同的信任材料(通常使用自签的证书授权)。这种信任材料通常以 JKSPKCS #12 格式的Java keystore文件的形式出现,或者它们可能是PEM编码的文本文件。这些文件类型都需要不同的处理。

一旦你有了信任(trust)材料,你需要把它转化为可以传递给 Java connection API 的东西。事情在这里会变得很困难,因为 connection API可以用各种方式配置:

  • 有些人希望你提供keystore和truststore java.security.KeyStore 实例。
  • 有些人希望你提供 javax.net.ssl.KeyManagerjavax.net.ssl.TrustManager 实例。
  • 有些人希望你能提供一个 javax.net.ssl.SSLContext 实例。

SSL也是相当低级的,所以你经常需要剥开几层抽象,以获得需要使用 java.securityjava.net.ssl 包的对象来配置的东西。例如,如果你想在Spring RestTemplate 上配置SSL,你需要深入到支持它的 ClientHttpRequestFactory。对于一个典型的Spring Boot应用来说,这可能是一个 HttpComponentsClientHttpRequestFactoryOkHttp3ClientHttpRequestFactorySimpleClientHttpRequestFactory。每一个都提供不同的配置API。

配置连接以使用SSL或TLS对Spring Boot来说并不新鲜,但团队决定从整体上考虑目前支持的内容,并寻找机会来改进和扩大支持。我们希望你会发现,Spring Boot 3.1使SSL配置变得更加容易。

引入 SSL Bundle

Spring Boot 3.1引入了 SSL bundle 的概念,用于配置和消费定制的SSL信任材料,如 keystore、证书和私钥。一旦配置好,就可以使用配置属性或API将 bundle 应用于一个或多个连接。

配置 SSL Bundle

用于配置SSL信任材料的属性在 application.yamlapplication.properties 文件中属于 spring.ssl.bundle 前缀。有两个顶层分组,以反映配置不同类型的信任材料所需的独特信息。

  • spring.ssl.bundle.jks 可以用来配置使用 Java keystore 文件的 bundle。
  • spring.sll.bundle.pem 可以用来配置使用PEM编码的文本文件的 bundle。

每种类型的一个或多个 bundle 可以被配置,每个配置的 bundle 都有一个用户提供的名字。这个名字在用 properties 应用 bundle 或用API检索 bundle 时使用。

下面的 application.yaml 文件示例显示了两个SSL bundle 的配置。第一个被命名为 server,定义了一个Java Keystore文件(PKCS #12格式),可用于保护一个嵌入式Web服务器。第二个被命名为 client,定义了一个带有PEM编码证书文件的trust store,可用于保护客户端与需要客户端认证的服务器的连接。

spring:
  ssl:
    bundle:
      jks:
        server:
          key:
            alias: "server"
          keystore:
            location: "classpath:server.p12"
            password: "secret"
            type: "PKCS12"
      pem:
        client:
          truststore:
            certificate: "classpath:client.crt"

参见 Spring Boot参考文档 以及 JksSslBundlePropertiesPemSslBundleProperties 类,以了解关于可用配置属性的更多细节。

使用自动配置的 SSL Bundle

Spring Boot使用 spring.ssl.bundle 属性来创建对象,提供对指定信任材料的访问。

如上所述,Java安全和SSL API提供了三个层次的抽象,以暴露从Java keystore或PEM文件读取的信任材料:

  • java.security.KeyStore 作为 keystores 和 truststore 的实例。
  • javax.net.ssl.KeyManagerjavax.net.ssl.TrustManager 实例。
  • javax.net.ssl.SSLContext 实例。

在最底层,你可能需要 truststore 和 keystore 对象来应用SSL到一个连接。这些对象可以使用 SslStoreBundle 接口访问,如图所示:

public interface SslStoreBundle {

	KeyStore getKeyStore();

	String getKeyStorePassword();

	KeyStore getTrustStore();

}

KeyManagerTrustManager 实例可以从 keystore 和 truststore 派生。这些可以使用 SslManagerBundle 接口来访问:

public interface SslManagerBundle {

	KeyManager[] getKeyManagers();

	KeyManagerFactory getKeyManagerFactory();

	TrustManager[] getTrustManagers();

	TrustManagerFactory getTrustManagerFactory();

}

最后,SSLContext 可以由 KeyManagerTrustManager 创建,并通过 createSslContext 工厂方法访问。

把所有这些放在一起,我们就有了一个 SslBundle 接口,可以访问各种不同的配置样式:

public interface SslBundle {

	SslStoreBundle getStores();

	SslManagerBundle getManagers();

	SSLContext createSslContext() {

}

SslBundle 中的全部方法列表见 源代码

配置的 SslBundles 的集合在 SslBundles Bean 中可用,可以自动注入到其他Spring Bean中:

public interface SslBundles {

	SslBundle getBundle(String bundleName) throws NoSuchSslBundleException;

}

一个使用 SslBundles 检索和应用 SSLContext 的例子可能是这样的:

@Component
public class MyComponent {

    public MyComponent(SslBundles sslBundles) {
        SslBundle sslBundle = sslBundles.getBundle("client");
        SSLContext sslContext = sslBundle.createSslContext();
        // do something with the created sslContext
    }

}

REST 客户端

在Spring Boot 3.1中启用的SSL功能的一个令人兴奋的新领域是REST客户端的配置。Spring Boot对定制 RestTemplateWebClient 的支持现在包括应用 SSL bundle 的能力,以确保客户端和REST服务之间的连接。

RestTemplateBuilder 有一个新的 setSslBundle() 方法,接受从自动配置的 SslBundles 中检索的SSL bundle ,如本例所示:

@Service
public class MyService {

    private final RestTemplate restTemplate;

    public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
        this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
    }

}

如本例所示,WebClientSsl 接口允许检索SSL bundle 并将其应用于 WebClient.Builder

@Service
public class MyService {

    private final WebClient webClient;

    public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
        this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
    }

}

数据服务连接

Spring Boot使配置用于从应用程序连接到数据服务的客户端库变得容易。这些客户端库是使用三个级别的 Java security 和SSL API中的任何一个进行配置的API的好例子。

在3.1之前,Spring Boot提供自动配置的许多数据服务都有某种形式的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

大多数服务都有一个 *.ssl.enabled 属性,它将在客户端库中使用Java运行时 cacerts 中包含的信任材料启用SSL支持。一个 *.ssl.bundle 属性应用了一个命名的SSL bundle ,以启用客户端库的SSL支持,并使用该 bundle 的自定义信任材料。这使得配置更加一致,并允许将相同的信任材料应用于多个连接,减少属性或YAML配置的数量。

为了提供这种水平的一致性,以前一些与SSL相关的属性已经被废弃。更多细节请参见 配置属性变更记录

JDBC连接是这个列表中的一个明显遗漏。我们计划在即将发布的Spring Boot中对JDBC连接采用 SSL bundle 方式。

嵌入式Web服务器

Spring Boot支持的所有嵌入式Web服务器都可以通过使用 server.ssl.* 属性配置为用SSL保护传入的连接。自Spring Boot诞生以来,就一直支持Java keystore文件,而自2.7以来,就一直支持PEM编码的文件。

server.ssl 前缀下的属性数量随着时间的推移而增加,由于缺乏结构,很难分辨哪些属性可以一起使用,哪些是相互排斥的。以前的 server.ssl.* 属性继续被支持,但新的 server.ssl.bundle 属性可用于将配置的 SSL bundle 应用到嵌入式Web服务器。

下面的两个例子在功能上是一样的:

server:
  ssl:
    key-alias: “server”
    key-password: “keysecret”
    key-store: "classpath:server.p12"
    key-store-password: "storesecret"
    client-auth: NEED    
spring:
  ssl:
    bundle:
      jks:
        web-server:
          key:
            alias: "server"
            password: “keysecret”
          keystore:
            location: "classpath:server.p12"
            password: "storesecret"
server:
  ssl:
    bundle: “web-server”
    client-auth: NEED

旧的结构更简洁,但新的结构减少了错误配置的机会,并允许在多个连接上使用相同的SSL bundle 。

management.server.sslspring.rsocket.server.ssl 属性也做了类似的修改。

未来的工作

我们真的希望你发现 SSL bundle 是Spring Boot 3.1的一个有用功能。如果你发现任何其他你认为我们应该添加SSL支持的技术,请提出一个 GitHub issue ,我们会在未来的版本中考虑它。


原文:https://spring.io/blog/2023/06/07/securing-spring-boot-applications-with-ssl/

1 个赞