java - 重新加载 java.net.http.HttpClient 的 SSLContext

标签 java ssl mutual-authentication java-http-client

我有一个使用 java.net.http.HttpClient 的程序,它是在 Java 11 中引入的,用于连接内部服务并向其发送请求。这些服务相互验证,均提供内部 CA 颁发的证书。

例如,

SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
KeyManager keys = /* load our cert and key */;
TrustManager trust = /* load our trusted CA */;
sslContext.init(keys, trust, secureRandom);

HttpClient.Builder builder = HttpClient.newBuilder().sslContext(sslContext);
HttpClient client = builder.build();

在我们的主机上,客户端的证书和私钥会定期轮换,比主机或应用程序有机会重新启动的频率更高。我希望能够在 HttpClientSSLContext 仍在运行时使用新的证书/ key 对重新加载,但看不到任何方法可以做到这一点.

HttpClient 构建完成后,仅提供 sslContext() getter 来检索 SSLContext。似乎没有 API 来设置新的。

还有其他机制可以实现这一点吗?

(我正在考虑类似于 Jetty 的 SslContextFactory#reload(SSLContext) 方法。)

最佳答案

我认为这个问题类似于How to renew keystore (SSLContext) in Spring Data Geode connections without restarting?我在那里提供的答案与此类似。

遗憾的是,此选项默认情况下不可用。将 SSLContext 提供给 HttpClient 并构建客户端后,您无法更改 SSLContext。您将需要创建一个新的 SSLContext 和一个新的 HttpClient,但有一个解决方法可以应用重新加载/更新。

我的一个项目遇到了同样的挑战,我通过使用自定义的信任管理器和 key 管理器解决了这个问题,该自定义信任管理器和 key 管理器包围了实际的信任管理器和 key 管理器,同时具有交换实际信任管理器和 key 管理器的能力。因此,如果您仍然想完成它而不需要重新创建 HttpClient 和 SSLContext,则可以使用以下设置:

SSLFactory baseSslFactory = SSLFactory.builder()
          .withDummyIdentityMaterial()
          .withDummyTrustMaterial()
          .withSwappableIdentityMaterial()
          .withSwappableTrustMaterial()
          .build();
          
HttpClient httpClient = HttpClient.newBuilder()
          .sslParameters(sslFactory.getSslParameters())
          .sslContext(sslFactory.getSslContext())
          .build()

Runnable sslUpdater = () -> {
   SSLFactory updatedSslFactory = SSLFactory.builder()
          .withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
          .withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
          .build();
    
   SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory)
};

// initial update of ssl material to replace the dummies
sslUpdater.run();
   
// update ssl material every hour    
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);

// execute https request
HttpResponse<String> response = httpClient.send(aRequest, HttpResponse.BodyHandlers.ofString());

请参阅此处了解此选项的文档:Swapping KeyManager and TrustManager at runtime

这里有一个实际的工作示例:Example swapping certificates at runtime with HttpUrlConnection

这里是服务器端示例:Example swapping certificates at runtime with Spring Boot and Jetty其他服务器也是可能的,例如 NettyVert.x只要他们可以使用 SSLContext、SSLServerSocketFactory、TrustManager 或 KeyManager

您可以使用以下方法将库添加到您的项目中:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>7.4.8</version>
</dependency>

您可以在此处查看完整文档和其他示例:GitHub - SSLContext Kickstart

顺便说一句,我需要添加一个小的免责声明,我是该库的维护者。

关于java - 重新加载 java.net.http.HttpClient 的 SSLContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69506869/

相关文章:

java - MigLayout 收缩行为

Delphi相互认证

spring-boot - Spring Boot x509 测试 - pcf

iphone - 使用 Python 连接到适用于 iPhone 的 APNS

firefox - 在 Firefox 和 Selenium 测试中自动化 SSL 客户端证书

ssl - WSO2 API 管理器,无效。无法找到到请求目标的有效证书路径

cUrl 相互认证

Java 休息框架

java - 无法摆脱 Android Studio 2.2 中的错误 "Duplicate files copied in APK META-INF/beans.xml"

java - 完美的 url 正则表达式