【问题标题】:Configuring SSL in Apache HttpAsyncClient在 Apache HttpAsyncClient 中配置 SSL
【发布时间】:2015-02-01 13:10:00
【问题描述】:

我需要设置一个支持 SSL 的 Apache HTTPAsyncClient。我使用此代码,但它似乎不起作用(获取“javax.net.ssl.SSLException: Received fatal alert: handshake_failure”)

    System.setProperty("javax.net.debug", "ssl,handshake");
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");

    KeyStore ts = KeyStore.getInstance("JKS");
    ts.load(loadStream("C:/TrustStore/cacerts"), "trustpass".toCharArray());
    KeyStore ks = KeyStore.getInstance("JKS");
    ks.load(loadStream("C:/KeyStore/SSL/keystore.SomeKey"), "keypass".toCharArray());

    SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(ts).loadKeyMaterial(ks, "somekey".toCharArray()).setSecureRandom(new SecureRandom());        
    SSLContext ssl = sslBuilder.build();

    PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT));        

    CloseableHttpAsyncClient clientHttps = HttpAsyncClientBuilder.create()
            .setConnectionManager(cm)    
            .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
            .setSSLContext(ssl)
            .build();

    RequestConfig.Builder b = RequestConfig.custom();        
    b.setProxy(new HttpHost("proxyHost", proxyPort));
    RequestConfig rc = b.build();

    clientHttps.start();

    HttpRequestBase req = new HttpPost("https://someurl");
    ((HttpEntityEnclosingRequestBase)req).setEntity(new StringEntity("somestring"));
    req.setConfig(rc);

    clientHttps.execute(req, new FutureCallback<HttpResponse>() {

        @Override
        public void failed(Exception ex) {
            System.out.println(ex);
        }

        @Override
        public void completed(HttpResponse result) {
            System.out.println(result);                
        }

        @Override
        public void cancelled() {
            System.out.println("Cancelled");                
        }
    });    

当使用 javax.net.ssl.HttpsURLConnection 来实现这一点时,它可以工作(如果需要,我可以附上相关代码)。

编辑

根据@ben75 的回答,我终于用下面的代码让它运行了

System.setProperty("javax.net.debug", "ssl,handshake");
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");

KeyStore ts = KeyStore.getInstance("JKS");
ts.load(loadStream("C:/TrustStore/cacerts"), "trustpass".toCharArray());
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(loadStream("C:/KeyStore/SSL/keystore.SomeKey"), "keypass".toCharArray());

SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(ts).loadKeyMaterial(ks, "somekey".toCharArray()).setSecureRandom(new SecureRandom());        
SSLContext ssl = sslBuilder.build();

SSLIOSessionStrategy s = new SSLIOSessionStrategy(ssl, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
RegistryBuilder<SchemeIOSessionStrategy> rb = RegistryBuilder.create();
rb.register("https", s).register("http", NoopIOSessionStrategy.INSTANCE);
PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT), rb.build());       

CloseableHttpAsyncClient clientHttps = HttpAsyncClientBuilder.create()
        .setConnectionManager(cm)
        .build();

RequestConfig.Builder b = RequestConfig.custom();        
b.setProxy(new HttpHost("proxyHost", proxyPort));
RequestConfig rc = b.build();

clientHttps.start();

HttpRequestBase req = new HttpPost("https://someurl");
((HttpEntityEnclosingRequestBase)req).setEntity(new StringEntity("somestring"));
req.setConfig(rc);

clientHttps.execute(req, new FutureCallback<HttpResponse>() {

    @Override
    public void failed(Exception ex) {
        System.out.println(ex);
    }

    @Override
    public void completed(HttpResponse result) {
        System.out.println(result);                
    }

    @Override
    public void cancelled() {
        System.out.println("Cancelled");                
    }
});    

【问题讨论】:

    标签: java ssl apache-httpclient-4.x apache-httpcomponents


    【解决方案1】:

    (我最近(在 Android 上)遇到了非常相似的问题,但我猜你犯的错误和我一样。)

    当您明确设置连接管理器时:builder.setConnectionManager(cm) sslContext 将被忽略。

    您可以做的是在 PoolingNHttpClientConnectionManager 中注入您的 SSLContext。

    为此,您可以使用此构造函数: PoolingNHttpClientConnectionManager(org.apache.http.nio.reactor.ConnectingIOReactor ioreactor, Registry iosessionFactoryRegistry)

    使用 iosessionFactoryRegistry 包含带有您的 SSLContext 的 SSLIOSessionStrategy 构建

    【讨论】:

    • 谢谢@ben75。为了完成您的回答,我不得不添加更多行(已编辑的问题)。
    • @MetaHnet 欢迎您。我刚刚编辑了您的问题以保留两个版本的代码,以便问题和答案对未来的读者仍然有意义
    • @ben75 我有一个特定的情况,我调用不同的端点(有些带有 sslContext,有些没有 sslContext)。这将在运行时决定,因此我无法预构建 sslContext 并将其分配给 PoolingNHttpClientConnectionManager。同样的方法应该是什么?
    【解决方案2】:

    这是一个工作示例:

    public SSLContext getSSLContext() {
        final TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
        try {
            final SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();
            sslContext.getServerSessionContext().setSessionCacheSize(1000);
            return sslContext;
        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
        }
        return null;
    }
    
    public Registry<SchemeIOSessionStrategy> getSSLRegistryAsync() {
        return RegistryBuilder.<SchemeIOSessionStrategy>create()
                .register("http", NoopIOSessionStrategy.INSTANCE)
                .register("https", new SSLIOSessionStrategy(
                        getSSLContext(), null, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)).build();
    }
    
    public PoolingNHttpClientConnectionManager getPoolingNHttpClientConnectionManager() {
        try {
            final PoolingNHttpClientConnectionManager connectionManager =
                    new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT), getSSLRegistryAsync());
            connectionManager.setMaxTotal(connectionPoolMax);
            connectionManager.setDefaultMaxPerRoute(connectionPoolMaxPerRoute);
    
            return connectionManager;
        } catch (IOReactorException e) {
        }
        return null;
    }
    
    public RequestConfig getRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(connectTimeout)
                .setSocketTimeout(socketTimeout)
                .setConnectionRequestTimeout(socketTimeout)
                .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                .build();
    }
    
    public CloseableHttpAsyncClient getHttpAsyncClient() {
        final CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
                .setConnectionManager(getPoolingNHttpClientConnectionManager())
                .setDefaultRequestConfig(getRequestConfig())
                .build();
        return httpAsyncClient;
    }
    

    【讨论】:

      【解决方案3】:

      我在异步模式下的例子:

           try {
               SSLContext sslContext = getSSLContext(X509Cert);
      
               CloseableHttpAsyncClient httpAsyncClient = getHttpAsyncClient(sslContext,HTTP_CLIENT_MAX_POOL_CONNECTIONS,HTTP_CLIENT_MAX_PER_ROUTE_CONN);
               setHttpAsyncClient(httpAsyncClient);
      
      
          } catch (Exception e) {
              LOG.error("Generic SSL Error Adapter: {} - {}", e.getMessage(), e.getLocalizedMessage());
          }
      
      }
      
      
      private SSLContext getSSLContext(X509CertificateValidator trustStrategy) {
          try {
              final SSLContext sslContext = SSLContexts.custom()
                      .loadTrustMaterial(null, trustStrategy)
                      .build();
              sslContext.getServerSessionContext().setSessionCacheSize(1000);
              return sslContext;
          } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
              LOG.error("SSL Context creation error: {} - {}", e.getMessage(), e.getLocalizedMessage());
          }
          return null;
      }
      
      
      private Registry<SchemeIOSessionStrategy> getSSLRegistryAsync(SSLContext sslContext) {
          Registry<SchemeIOSessionStrategy> defaultRegistry = RegistryBuilder.<SchemeIOSessionStrategy> create()
                  .register("http", NoopIOSessionStrategy.INSTANCE)
                  .register("https", new SSLIOSessionStrategy(sslContext, NoopHostnameVerifier.INSTANCE))
                  .build();
      
          return defaultRegistry;
      
      }
      
      private PoolingNHttpClientConnectionManager getPoolingNHttpClientConnectionManager(SSLContext sslContext, int connectionPoolMax, int connectionPoolMaxPerRoute) {
          try {
              PoolingNHttpClientConnectionManager connectionManager =
                      new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT), getSSLRegistryAsync(sslContext));
              connectionManager.setMaxTotal(connectionPoolMax);
              connectionManager.setDefaultMaxPerRoute(connectionPoolMaxPerRoute);
              return connectionManager;
          } catch (IOReactorException e) {
              LOG.error("NHttp Connectio Manager error: {} - {}", e.getMessage(), e.getLocalizedMessage());
          }
          return null;
      }
      
      
      public CloseableHttpAsyncClient getHttpAsyncClient(SSLContext sslContext, int connectionPoolMax, int connectionPoolMaxPerRoute) {
          final CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
                  .setConnectionManager(getPoolingNHttpClientConnectionManager(sslContext, connectionPoolMax, connectionPoolMaxPerRoute))
                  //.setSSLHostnameVerifier(new NoopHostnameVerifier())
                  .build();
          return httpAsyncClient;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-01
        • 2016-04-13
        • 2011-05-06
        • 1970-01-01
        • 2016-04-07
        • 1970-01-01
        • 1970-01-01
        • 2017-11-26
        相关资源
        最近更新 更多