HttpClient with SSL

SSLを使用したHttpClient

1. 概要

この記事では、configure the Apache HttpClient 4 with “Accept All” SSL supportを実行する方法を説明します。 目標は単純です。有効な証明書を持たないHTTPS URLを使用します。

深く掘り下げて、HttpClientでできる他のクールなことを学びたい場合は、the main HttpClient guideに進んでください。

参考文献:

HttpClient接続管理

Apache HttpClient 4との接続を開く、管理する、閉じる方法。

高度なHttpClient設定

高度なユースケース向けのHttpClient構成。

Apache HttpClient 4でカスタムCookieを送信する方法。

2. SSLPeerUnverifiedException

HttpClientでSSLを構成しないと、次のテスト(HTTPS URLを使用)は失敗します。

public class RestClientLiveManualTest {

    @Test(expected = SSLPeerUnverifiedException.class)
    public void whenHttpsUrlIsConsumed_thenException()
      throws ClientProtocolException, IOException {

        CloseableHttpClient httpClient = HttpClients.createDefault();
        String urlOverHttps
          = "https://localhost:8082/httpclient-simple";
        HttpGet getMethod = new HttpGet(urlOverHttps);

        HttpResponse response = httpClient.execute(getMethod);
        assertThat(response.getStatusLine().getStatusCode(), 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)
    ...

javax.net.ssl.SSLPeerUnverifiedException exceptionは、URLに対して有効な信頼のチェーンを確立できなかった場合に発生します。

3. SSLの構成–すべて受け入れる(HttpClient <4.3)

次に、有効性に関係なくすべての証明書チェーンを信頼するようにHTTPクライアントを構成しましょう。

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    CloseableHttpClient httpClient = (CloseableHttpClient) requestFactory.getHttpClient();

    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLSocketFactory sf = new SSLSocketFactory(acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 8443, sf));

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

新しいTrustStrategyoverriding the standard certificate verification processになりました(構成されたトラストマネージャーに相談する必要があります)–テストに合格し、the client is able to consume the HTTPS URLになりました。

4. SSLの構成–すべて受け入れる(HttpClient 4.4以降)

新しいHTTPClientにより、デフォルトのSSLホスト名検証機能が強化され、再設計されました。 また、SSLConnectionSocketFactoryRegistryBuilderの導入により、SSLSocketFactoryを簡単に構築できます。 したがって、上記のテストケースは次のように記述できます。

@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 socketFactoryRegistry =
      RegistryBuilder. 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 response = new RestTemplate(requestFactory)
      .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

5. SSLを使用したSpringRestTemplate(HttpClient <4.3)

SSLサポートを使用して生のHttpClientを構成する方法を確認したので、より高いレベルのクライアントであるSpringRestTemplateを見てみましょう。

SSLが設定されていない場合、次のテストは期待どおりに失敗します。

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

それでは、SSLを構成しましょう。

@Test
public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException()
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory
      = new HttpComponentsClientHttpRequestFactory();
    DefaultHttpClient httpClient
      = (DefaultHttpClient) requestFactory.getHttpClient();
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true
    SSLSocketFactory sf = new SSLSocketFactory(
      acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry()
      .register(new Scheme("https", 8443, sf));

    String urlOverHttps
      = "https://localhost:8443/httpclient-simple/api/bars/1";
    ResponseEntity response = new RestTemplate(requestFactory).
      exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

ご覧のとおり、これはvery similar to the way we configured SSL for the raw HttpClientです。SSLサポートを使用してリクエストファクトリを構成してから、この事前構成済みファクトリを渡すテンプレートをインスタンス化します。

6. SSLを使用したSpringRestTemplate(HttpClient 4.4)

また、同じ方法を使用してRestTemplateを構成できます。

@Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect()
throws ClientProtocolException, IOException {
    CloseableHttpClient httpClient
      = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    HttpComponentsClientHttpRequestFactory requestFactory
      = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

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

7. 結論

このチュートリアルでは、証明書に関係なくHTTPS URLを使用できるようにApache HttpClientのSSLを構成する方法について説明しました。 SpringRestTemplateの同じ構成も示されています。

ただし、理解しておくべき重要なことは、this strategy entirely ignores certificate checkingであるということです。これにより、安全性が低下し、意味のある場合にのみ使用されます。

これらの例の実装はthe GitHub projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。