重新加载 java.net.http.HttpClient 的 SSLContext
Reloading a java.net.http.HttpClient's SSLContext
我有一个程序利用 Java 11 中引入的 java.net.http.HttpClient
来连接内部服务并向其发送请求。这些服务相互验证,都提供由内部 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();
在我们的主机上,客户端的证书和私钥会定期轮换,比主机或应用程序有机会重新启动的频率要高。我希望能够在 HttpClient
的 SSLContext
仍然是 运行 时用新的 cert/key 对重新加载它,但看不到任何方法.
构建HttpClient
后,它只提供一个sslContext()
getter来检索SSLContext
。好像没有API设置新的
是否有任何其他机制可以实现此目的?
(我在想 Jetty 的 SslContextFactory#reload(SSLContext)
方法。)
我认为这个问题与 我提供的答案与此问题相似。
不幸的是,默认情况下此选项不可用。将 SSLContext 提供给 HttpClient 并构建客户端后,您将无法更改 SSLContext。您将需要创建一个新的 SSLContext 和一个新的 HttpClient。
我的一个项目遇到了同样的挑战,我通过使用自定义的 trustmanager 和 keymanager 解决了它,它环绕实际的 trustmanager 和 keymanager,同时具有交换实际的 trustmanager 和 keymanager 的能力。因此,如果您仍想完成它而无需重新创建 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();
// swap identity and trust materials and reuse existing http client
KeyManagerUtils.swapKeyManager(baseSslFactory.getKeyManager().get(), updatedSslFactory.getKeyManager().get());
TrustManagerUtils.swapTrustManager(baseSslFactory.getTrustManager().get(), updatedSslFactory.getTrustManager().get());
// Cleanup old ssl sessions by invalidating them all. Forces to use new ssl sessions which will be created by the swapped KeyManager/TrustManager
SSLSessionUtils.invalidateCaches(baseSslFactory.getSslContext());
};
// initial update of ssl material to replace the dummies
Executors.newSingleThreadExecutor().submit(sslUpdater);
// 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 Also other servers are possible such as Netty or Vert.x 只要他们可以使用 SSLContext、SSLServerSocketFactory、TrustManager 或 KeyManager
您可以将库添加到您的项目中:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.4.3</version>
</dependency>
您可以在此处查看完整文档和其他示例:GitHub - SSLContext Kickstart
顺便说一下,我需要添加一个小的免责声明,我是库的维护者。
我有一个程序利用 Java 11 中引入的 java.net.http.HttpClient
来连接内部服务并向其发送请求。这些服务相互验证,都提供由内部 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();
在我们的主机上,客户端的证书和私钥会定期轮换,比主机或应用程序有机会重新启动的频率要高。我希望能够在 HttpClient
的 SSLContext
仍然是 运行 时用新的 cert/key 对重新加载它,但看不到任何方法.
构建HttpClient
后,它只提供一个sslContext()
getter来检索SSLContext
。好像没有API设置新的
是否有任何其他机制可以实现此目的?
(我在想 Jetty 的 SslContextFactory#reload(SSLContext)
方法。)
我认为这个问题与
不幸的是,默认情况下此选项不可用。将 SSLContext 提供给 HttpClient 并构建客户端后,您将无法更改 SSLContext。您将需要创建一个新的 SSLContext 和一个新的 HttpClient。
我的一个项目遇到了同样的挑战,我通过使用自定义的 trustmanager 和 keymanager 解决了它,它环绕实际的 trustmanager 和 keymanager,同时具有交换实际的 trustmanager 和 keymanager 的能力。因此,如果您仍想完成它而无需重新创建 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();
// swap identity and trust materials and reuse existing http client
KeyManagerUtils.swapKeyManager(baseSslFactory.getKeyManager().get(), updatedSslFactory.getKeyManager().get());
TrustManagerUtils.swapTrustManager(baseSslFactory.getTrustManager().get(), updatedSslFactory.getTrustManager().get());
// Cleanup old ssl sessions by invalidating them all. Forces to use new ssl sessions which will be created by the swapped KeyManager/TrustManager
SSLSessionUtils.invalidateCaches(baseSslFactory.getSslContext());
};
// initial update of ssl material to replace the dummies
Executors.newSingleThreadExecutor().submit(sslUpdater);
// 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 Also other servers are possible such as Netty or Vert.x 只要他们可以使用 SSLContext、SSLServerSocketFactory、TrustManager 或 KeyManager
您可以将库添加到您的项目中:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.4.3</version>
</dependency>
您可以在此处查看完整文档和其他示例:GitHub - SSLContext Kickstart
顺便说一下,我需要添加一个小的免责声明,我是库的维护者。