当前位置: 首页 > 知识库问答 >
问题:

WebFlow Webclient Netty,如何使用SslContext与多个客户端证书?

濮金鑫
2023-03-14

关于Netty、Spring Webflux以及如何向多个下游系统发送http请求的小问题,当每个下游系统都需要MTL并且需要不同的客户端证书来向每个系统发送请求时,请回答?

到目前为止,我在我的Java11 Spring WebFlow 2.4.2应用程序中发送请求是:

@Bean
    @Primary
    public WebClient getWebClient() {
        return WebClient.create().mutate().defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).clientConnector(new ReactorClientHttpConnector(HttpClient.create().wiretap(true).secure(sslContextSpec -> sslContextSpec.sslContext(getSslContext())))).build();
    }

对于Netty SslContext(顺便说一句,它不是apache SslContext)

 public SslContext getSslContext() {
        try {
            final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            try (InputStream file = new FileInputStream(keyStorePath)) {
                final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
                keyStore.load(file, keyStorePassPhrase.toCharArray());
                keyManagerFactory.init(keyStore, keyPassPhrase.toCharArray());
            }

            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            try (InputStream trustStoreFile = new FileInputStream(trustStorePath)) {
                final KeyStore trustStore = KeyStore.getInstance(trustStoreType);
                trustStore.load(trustStoreFile, trustStorePassPhrase.toCharArray());
                trustManagerFactory.init(trustStore);
            }

            return SslContextBuilder.forClient().keyManager(keyManagerFactory).trustManager(trustManagerFactory).build();
        } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException | UnrecoverableKeyException e) {
            return null;
        }
    }

当我们只需要向一个下游发送请求时,这甚至可以正常工作。

即使有多个下游,并且它们接受相同的客户机证书,这也是可行的!

但当每个下游都要求我使用各自的客户端证书时,问题就出现了。

请问如何做到这一点?

非常感谢。

共有1个答案

邰伟彦
2023-03-14

最直接的解决方案是为每个下游api使用特定的客户端。并为每个客户端配置其特定的客户端密钥和信任材料。

但您的问题是:如何将SslContext与多个客户端证书一起使用?

所以我想给你一些代码示例来建立一个有效的设置。但简短的答案是:是的,这是可能的!

长话短说,您需要一些额外的配置才能使其工作。基本上,您需要做的是从keystore-1创建一个keymanagerfactory,并从keymanagerfactory获取keymanager,然后对其他两个keystore重复该操作。之后,您将拥有3个密钥管理器。下一步是有一种特殊的密钥管理器,可以提供给Netty SslContext。这种特殊类型的keymanager能够遍历您之前创建的3个keymanager,它将选择正确的密钥材料与服务器通信。您需要的是一个CompositeKeyManager和CompositeTrustManager,这在下面的stackoverflow答案中提到:在JVM中注册多个密钥库

实际的代码片段如下所示。我忽略了使用inputstream加载文件和创建密钥库文件以及创建keymanagerfactory,因为您已经知道如何做到这一点。

KeyManager keyManagerOne = keyManagerFactoryOne.getKeyManagers()[0]
KeyManager keyManagerTwo = keyManagerFactoryTwo.getKeyManagers()[0]
KeyManager keyManagerThree = keyManagerFactoryThree.getKeyManagers()[0]

List<KeyManager> keyManagers = new ArrayList<>();
keyManagers.add(keyManagerOne);
keyManagers.add(keyManagerTwo);
keyManagers.add(keyManagerThree);

CompositeX509KeyManager baseKeyManager = new CompositeX509KeyManager(keyManagers);

//repeat the same for the trust material
TrustManager trustManagerOne = trustManagerFactoryOne.getTrustManagers()[0]
TrustManager trustManagerTwo = trustManagerFactoryTwo.getTrustManagers()[0]
TrustManager trustManagerThree = trustManagerFactoryThree.getTrustManagers()[0]

List<TrustManager> trustManagers = new ArrayList<>();
trustManagers.add(trustManagerOne);
trustManagers.add(trustManagerTwo);
trustManagers.add(trustManagerThree);

CompositeX509TrustManager baseTrustManager = new CompositeX509TrustManager(trustManagers);

SslContext sslContext = SslContextBuilder.forClient()
    .keyManager(baseKeyManager)
    .trustManager(baseTrustManager)
    .build();

上面的代码应该使您能够为单个客户端使用多个密钥和信任。该客户端将能够使用不同的密钥和信任材料与不同的下游api进行通信。

这种设置的缺点是,您需要将CompositeKeyManager和CompositeTrustManager复制并粘贴到您的代码库中,并且该设置有点冗长。Java没有为这个用例提供现成的东西。

如果您想要一个有点简单的设置,我建议您使用下面的代码片段:

import io.netty.handler.ssl.SslContext;
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.NettySslUtils;

public class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withIdentityMaterial(keyStorePathOne, password)
                .withIdentityMaterial(keyStorePathTwo, password)
                .withIdentityMaterial(keyStorePathThree, password)
                .withTrustMaterial(trustStorePathOne, password)
                .withTrustMaterial(trustStorePathTwo, password)
                .withTrustMaterial(trustStorePathThree, password)
                .build();

        SslContext sslContext = NettySslUtils.forClient(sslFactory).build();
    }

}

我需要提供一些免责声明,我是上面代码片段库的维护者。该库在这里可用:GitHub-SSLContext Kickstart,它在我前面提到的选项2的封面下使用相同的CompositeKeyManager和CompositeTrustManager。

您可以使用以下代码片段将其添加到pom中:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-netty</artifactId>
    <version>6.6.0</version>
</dependency>
 类似资料:
  • 问题内容: 我想要求基于Java 1.7内置HttpsServer的服务器进行客户端证书认证。 我似乎找不到任何使服务器无法通过身份验证的方法。无论客户端证书是受信任的,未知的还是完全不存在的,它都会愉快地为所有旧客户端提供数据。 我对文档的阅读建议,如果不信任客户端,则设置HttpsParameters.setNeedClientAuth(true)应该会导致身份验证失败。我发现有类似问题的人的

  • 我在Jmeter中执行API时遇到了问题。我们的API中有客户端证书。pfx格式。我已经把它换成了。jks,并在系统中进行了更新。jmeter的特性。在jmeter中,我创建了一个csv文件来获取创建的别名。但是,错误显示为 错误:响应消息:非HTTP响应消息:java.lang.IllegalArgumentException:未找到别名的证书:'certalias' 以下是我的别名信息:别名:

  • 我正在使用Netty4创建一个需要为多个客户端连接提供服务的服务器。ServerBootstrap由父线程组和工作线程组构成。根据ServerBootStrap上的文档。group()方法it “为父(接收器)和子(客户端)设置EventLoopGroup。这些EventLoopGroup用于处理SocketChannel和Channel的所有事件和IO。” 据我所知,ParentExecutor

  • 问题内容: 这不是设计上应该发生的事情,但是出于安全考虑,我想知道,如果有多个证书与某个CA签署的要求相匹配,那么如何将“正确的”证书发送到服务器? 我正在使用一个简单的SSL JAVA示例客户端,连接到Apache HTTPD。 我尝试用4个证书进行测试,每次删除选择的证书,并记下下一个是谁。除了证书的“ sha256”的词典顺序之外,我找不到合理的逻辑(即日期,别名等)。在我看来这不太可能…

  • 我有一个Java服务器应用程序,它通过TLS从GRPC客户端获取数据并在服务器上处理。现在我想使用多个客户端。要向客户端分配接收到的事件,我想读取客户端证书并使用证书的DN分配事件。如何获取客户端证书? 我发现这个构建服务器:https://github.com/grpc/grpc-java/blob/master/SECURITY.md#mutual-tls 然后你应该实现一个服务器拦截器 }

  • 问题内容: RMI是否自己处理多个客户端?即 是否可以同时使用多个客户端的服务器功能? 如果没有,我该怎么做? 如果是,它如何工作?每次 调用 都会创建一个新线程吗?如果一个客户端阻止了该功能,那么下一个客户端会发生什么?等等 问题答案: 是 这个怎么运作?每次调用都会创建一个新线程吗?如果一个客户端阻止了该功能,那么下一个客户端会发生什么?等等 它为每个客户端连接创建一个线程。 如果一个客户端调