Apache Commons AsyncClient - 忽略证书 - SSLPeerUnverifiedException

Apache Commons AsyncClient - Ignore Certificates - SSLPeerUnverifiedException

以为我在我的 Http 客户端中禁用了证书检查,但一直在 SSLPeerUnverifiedException

这是我的客户:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Stream;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class ApacheCommonsAsyncClient implements IMakeHttpRequests {
    private static final Logger LOGGER = LoggerFactory.getLogger(PaxHttpClient.class);
    private static final int MAX_POOL_SIZE = 100;
    private static final int MAX_CONN_PER_ROUTE = 10;

    private final CloseableHttpAsyncClient httpClient;

    ApacheCommonsAsyncClient() {
        final RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000).setSocketTimeout(0).build();
        final Header doNotKeepAlive = new BasicHeader("Connection: keep-alive", "false");
        final Header closeConnection = new BasicHeader("Connection", "close");

        try {
            final SSLContextBuilder sslContextBuilder = new SSLContextBuilder().loadTrustMaterial(null,
                    new TrustSelfSignedStrategy());
            final ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
            final PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(ioReactor);

            // @formatter:off
            this.httpClient = HttpAsyncClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .setKeepAliveStrategy((httpResponse, httpContext) -> 0)
                    .setDefaultHeaders(Arrays.asList(doNotKeepAlive, closeConnection))
                    .setConnectionManager(cm)
                    .setSSLContext(sslContextBuilder.build())
                    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                    .setMaxConnTotal(MAX_POOL_SIZE)
                    .setMaxConnPerRoute(MAX_CONN_PER_ROUTE)
                    .build();
            // @formatter:on

            this.httpClient.start();
        } catch (final GeneralSecurityException | IOReactorException e) {
            throw new RuntimeException(e);
        }
    }
}

异常:

java.util.concurrent.ExecutionException: javax.net.ssl.SSLPeerUnverifiedException: Host name '<public-dns>' does not match the certificate subject provided by the peer (CN=*.<domain>.com, O="<org>", L=<location>, ST=<state>, C=<country>)
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
    at com.paxata.performance.App.run(App.java:30)
    at com.paxata.performance.Bootstrap.main(Bootstrap.java:43)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Host name '<public-dns>' does not match the certificate subject provided by the peer (CN=*.<domain>.com, O="<org>", L=<location>, ST=<state>, C=<country>)
    at org.apache.http.nio.conn.ssl.SSLIOSessionStrategy.verifySession(SSLIOSessionStrategy.java:208)
    at org.apache.http.nio.conn.ssl.SSLIOSessionStrategy.verify(SSLIOSessionStrategy.java:188)
    at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:367)
    at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:508)
    at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:120)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:588)
    at java.lang.Thread.run(Thread.java:748)

以及我引入的依赖版本:

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpmime</artifactId>
        <version>4.5.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
        <version>4.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpasyncclient</artifactId>
        <version>4.1.3</version>
    </dependency>

传递给构建器的连接管理器实例取代所有连接管理参数,例如 SSL 和池设置。

有两种补救方法。

  1. 让构建器构造并初始化一个连接管理器

final SSLContextBuilder sslContextBuilder = new SSLContextBuilder().loadTrustMaterial(null,
        new TrustSelfSignedStrategy());
this.httpClient = HttpAsyncClients.custom()
        .setDefaultRequestConfig(requestConfig)
        .setKeepAliveStrategy((httpResponse, httpContext) -> 0)
        .setDefaultHeaders(Arrays.asList(doNotKeepAlive, closeConnection))
        .setSSLContext(sslContextBuilder.build())
        .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
        .setMaxConnTotal(MAX_POOL_SIZE)
        .setMaxConnPerRoute(MAX_CONN_PER_ROUTE)
        .build();
  1. 在传递给构建器之前配置连接管理器

final SSLContextBuilder sslContextBuilder = new SSLContextBuilder().loadTrustMaterial(null,
        new TrustSelfSignedStrategy());
final PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(
        new DefaultConnectingIOReactor(), 
        RegistryBuilder.<SchemeIOSessionStrategy>create()
            .register("http", NoopIOSessionStrategy.INSTANCE)
            .register("https", new SSLIOSessionStrategy(sslContextBuilder.build(), NoopHostnameVerifier.INSTANCE))
            .build());
cm.setMaxTotal(MAX_POOL_SIZE);
cm.setDefaultMaxPerRoute(MAX_CONN_PER_ROUTE);

this.httpClient = HttpAsyncClients.custom()
        .setDefaultRequestConfig(requestConfig)
        .setKeepAliveStrategy((httpResponse, httpContext) -> 0)
        .setDefaultHeaders(Arrays.asList(doNotKeepAlive, closeConnection))
        .setConnectionManager(cm)
        .build();

推荐前者,除非有非常充分的理由选择后者。

PS:您不想禁用连接持久性