51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

配置 Apache HttpClient 信任所有 SSL 证书

1、概览 {#1概览}

本文将带你了解如何配置 Apache HttpClient 45 以支持 "Accept All"(接受所有)SSL。目标很简单 - 信任所有证书,包括无效的 SSL 证书。

2、SSLPeerUnverifiedException {#2sslpeerunverifiedexception}

如果不在 HttpClient 上配置 SSL,下面的测试(使用 HTTPS URL)就会失败:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    String urlOverHttps = "https://localhost:8082/httpclient-simple";
    HttpGet getMethod = new HttpGet(urlOverHttps);
assertThrows(SSLPeerUnverifiedException.class, () -> {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpResponse response = httpClient.execute(getMethod, new CustomHttpClientResponseHandler());
    assertThat(response.getCode(), equalTo(200));
});

}

确切的异常是:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126)
    ...

如果无法为 URL 建立有效的信任链(Trust Chain),就会出现 javax.net.ssl.SSLPeerUnverifiedException 异常。

3、配置 SSL - Accept All(HttpClient 5) {#3配置-ssl---accept-allhttpclient-5}

现在,配置 HTTP 客户端信任所有证书链,无论其是否有效:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException, IOException {
final HttpGet getMethod = new HttpGet(HOST_WITH_SSL);

final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; final SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(null, acceptingTrustStrategy) .build(); final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()) .build();

final BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry);

try( CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .build();

CloseableHttpResponse response = (CloseableHttpResponse) httpClient
    .execute(getMethod, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));

}

}

通过新的 TrustStrategy 覆盖标准的证书验证过程(应该考虑配置 Trust Manager),现在测试通过,客户端能够使用 HTTPS URL。

4、配置 SSL - Accept All(HttpClient 4.5) {#4配置-ssl---accept-allhttpclient-45}

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
  throws GeneralSecurityException {
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, 
      NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> socketFactoryRegistry = 
  RegistryBuilder.<ConnectionSocketFactory> create()
  .register("https", sslsf)
  .register("http", new PlainConnectionSocketFactory())
  .build();

BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .setConnectionManager(connectionManager).build();

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); ResponseEntity<String> response = new RestTemplate(requestFactory) .exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200));

}

5、RestTemplate 配置 SSL(HttpClient 5) {#5resttemplate-配置-sslhttpclient-5}

在了解了如何给原始 HttpClient 配置 SSL 之后,来看看更高级别的客户端 - Spring RestTemplate

在未配置 SSL 的情况下,以下测试会失败:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    final String urlOverHttps = "https://localhost:8443/httpclient-simple/api/bars/1";
assertThrows(ResourceAccessException.class, () -> {
    final ResponseEntity<String> response = new RestTemplate()
        .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
});

}

配置 SSL:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException {
final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
final SSLContext sslContext = SSLContexts.custom()
    .loadTrustMaterial(null, acceptingTrustStrategy)
    .build();
final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
    .register("https", sslsf)
    .register("http", new PlainConnectionSocketFactory())
    .build();

final BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry); final CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .build();

final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); final ResponseEntity<String> response = new RestTemplate(requestFactory) .exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode() .value(), equalTo(200));

}

如你所见,这与为原始 HttpClient 配置 SSL 的方式非常相似 - 使用 SSL 支持配置 RequestFactory ,然后通过这个预配置的 Factory 实例化 Template

6、RestTemplate 配置 SSL(HttpClient 4.5) {#6resttemplate-配置-sslhttpclient-45}

@Test
void givenAcceptingAllCertificates_whenUsingRestTemplate_thenCorrect() {
    final CloseableHttpClient httpClient = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    final HttpComponentsClientHttpRequestFactory requestFactory
        = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
final ResponseEntity<String> response = new RestTemplate(requestFactory).exchange(urlOverHttps, HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode().value(), equalTo(200));

}

7、总结 {#7总结}

本文介绍了如何为 Apache HttpClient 配置 SSL,使其信任所有 HTTPS URL(包括无效证书),还介绍了如何为 Spring RestTemplate 进行相同的配置。

不过,需要注意,这种策略完全忽略了证书检查,因此不安全,只能在合理的情况下使用。


Ref:https://www.baeldung.com/httpclient-ssl

赞(6)
未经允许不得转载:工具盒子 » 配置 Apache HttpClient 信任所有 SSL 证书