Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何配置反应式WebClient以使用双向TLS?_Java_Spring_Ssl_Reactive Programming - Fatal编程技术网

Java 如何配置反应式WebClient以使用双向TLS?

Java 如何配置反应式WebClient以使用双向TLS?,java,spring,ssl,reactive-programming,Java,Spring,Ssl,Reactive Programming,我正在尝试将反应式WebClient配置为使用双向TLS。我用它作为参考。(使用WebClient自定义程序的,而不是使用不安全的TrustManager的) 我仔细检查了客户端和服务器端的密钥库和信任库,但服务器发回一个错误,表示客户端没有提供任何证书: @Bean WebClientCustomizer configureWebclient(@Value("${server.ssl.trust-store}") String trustStorePath, @Va

我正在尝试将反应式WebClient配置为使用双向TLS。我用它作为参考。(使用WebClient自定义程序的,而不是使用不安全的TrustManager的)

我仔细检查了客户端和服务器端的密钥库和信任库,但服务器发回一个错误,表示客户端没有提供任何证书:

  @Bean
  WebClientCustomizer configureWebclient(@Value("${server.ssl.trust-store}") String trustStorePath, @Value("${server.ssl.trust-store-password}") String trustStorePass,
      @Value("${server.ssl.key-store}") String keyStorePath, @Value("${server.ssl.key-store-password}") String keyStorePass, @Value("${server.ssl.key-alias}") String keyAlias) {

    return new WebClientCustomizer() {

      @Override
      public void customize(Builder webClientBuilder) {
        SslContext sslContext;
        try {
          KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
          trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());

          List<Certificate> certificateCollcetion = Collections.list(trustStore.aliases()).stream().filter(t -> {
            try {
              return trustStore.isCertificateEntry(t);
            } catch (KeyStoreException e1) {
              throw new RuntimeException("Error reading truststore", e1);
            }
          }).map(t -> {
            try {
              return trustStore.getCertificate(t);
            } catch (KeyStoreException e2) {
              throw new RuntimeException("Error reading truststore", e2);
            }
          }).collect(Collectors.toList());

          KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());
          sslContext = SslContextBuilder.forClient()
              .keyManager((PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray()))
              .trustManager((X509Certificate[]) certificateCollcetion.toArray(new X509Certificate[certificateCollcetion.size()]))
              .build();
        } catch (Exception e) {
          log.error("Error creating web client", e);
          throw new RuntimeException(e);
        }
        ClientHttpConnector connector = new ReactorClientHttpConnector((opt) -> {
          opt.sslContext(sslContext);
        });
        webClientBuilder.clientConnector(connector);
      }
    };
  }
@Bean
WebClient自定义程序配置WebClient(@Value(${server.ssl.trust store})字符串trustStorePath,@Value(${server.ssl.trust store password})字符串trustStorePass,
@Value(${server.ssl.key store})字符串keystrepath,@Value(${server.ssl.key store password}”)字符串keystrepass,@Value(${server.ssl.key alias})字符串keystepas){
返回新的WebClientCustomizer(){
@凌驾
public void自定义(构建器webClientBuilder){
SslContext SslContext;
试一试{
KeyStore trustStore=KeyStore.getInstance(KeyStore.getDefaultType());
load(新文件输入流(ResourceUtils.getFile(trustStorePath)),trustStorePass.tocharray();
List certificateCollection=Collections.List(trustStore.aliases()).stream().filter(t->{
试一试{
返回trustStore.isCertificateEntry(t);
}捕获(KeyStoreException e1){
抛出新的运行时异常(“读取信任库时出错”,e1);
}
}).map(t->{
试一试{
返回trustStore.getCertificate(t);
}捕获(KeyStoreException e2){
抛出新的运行时异常(“读取信任库时出错”,e2);
}
}).collect(Collectors.toList());
KeyStore KeyStore=KeyStore.getInstance(KeyStore.getDefaultType());
load(新文件输入流(ResourceUtils.getFile(keystrepath)),keystrepass.toCharArray();
sslContext=SslContextBuilder.forClient()
.keyManager((PrivateKey)keyStore.getKey(keyalis,keyStorePass.toCharArray())
.trustManager((X509Certificate[])CertificateCollection.toArray(新的X509Certificate[CertificateCollection.size()))
.build();
}捕获(例外e){
log.error(“创建web客户端时出错”,e);
抛出新的运行时异常(e);
}
ClientHttpConnector连接器=新的反应器客户端HttpConnector((可选)->{
选择sslContext(sslContext);
});
webClientBuilder.clientConnector(连接器);
}
};
}

是否有人可以分享有关如何正确配置反应式WebClient以使用双向TLS的见解?

由于某些原因,当ssl上下文按如下方式构建时,服务器将不接受客户端证书:

sslContext = SslContextBuilder.forClient()
          .keyManager((PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray()))
          .trustManager((X509Certificate[]) certificateCollcetion.toArray(new X509Certificate[certificateCollcetion.size()]))
          .build();
要解决此问题,我必须初始化KeyManagerFactory:

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, keyStorePass.toCharArray());
然后,我用工厂初始化了ssl上下文:

SslContext sslContext = SslContextBuilder.forClient()
                    .keyManager(keyManagerFactory)
                    .trustManager((X509Certificate[]) certificateCollection.toArray(new X509Certificate[certificateCollection.size()]))
                    .build();
之后,服务器接受了证书,我就可以连接了

总之,我使用了这个更干净的解决方案,它将工厂用于密钥存储和信任存储:

@Value("${server.ssl.trust-store}")
String trustStorePath;
@Value("${server.ssl.trust-store-password}")
String trustStorePass;
@Value("${server.ssl.key-store}")
String keyStorePath;
@Value("${server.ssl.key-store-password}")
String keyStorePass;

@Bean
public WebClient create2WayTLSWebClient() {

    ClientHttpConnector connector = new ReactorClientHttpConnector(
            options -> {
                options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
                options.sslContext(get2WaySSLContext());
            }
    );

    return WebClient.builder()
            .clientConnector(connector)
            .build();

}

private SslContext get2WaySSLContext() {

    try {

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, keyStorePass.toCharArray());

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        trustManagerFactory.init(trustStore);

        return SslContextBuilder.forClient()
                .keyManager(keyManagerFactory)
                .trustManager(trustManagerFactory)
                .build();

    } catch (Exception e) {
        logger.error("Error creating 2-Way TLS WebClient. Check key-store and trust-store.");
        e.printStackTrace();
    }

    return null;
}

请注意,如果您使用的是Spring 5.1或更高版本,此特定实现将无法工作,因为您无法再将HttpClientOptions传递给ReactorClientHttpConnector。用作该配置的指南。但是,此答案中的代码内容仍应适用于此类配置。

感谢您提供详细的文档!