1. 개요

이 문서에서는 "모두 수락" SSL 지원으로 Apache HttpClient 4를 구성하는 방법을 보여줍니다 . 목표는 간단합니다. 유효한 인증서가 없는 HTTPS URL을 사용하는 것입니다.

더 깊이 파고들고 HttpClient로 할 수 있는 다른 멋진 것들을 배우고 싶다면 – 메인 HttpClient 사용방법(예제) 로 넘어가십시오 .

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

이제 새로운 TrustStrategy 가 표준 인증서 확인 프로세스 (구성된 신뢰 관리자에게 문의해야 함)를 재정의하므로 이제 테스트가 통과되고 클라이언트가 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<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. SSL을 사용하는 Spring RestTemplate (HttpClient < 4.3)

이제 SSL 지원으로 원시 HttpClient 를 구성하는 방법을 살펴보았 으므로 더 높은 수준의 클라이언트인 Spring RestTemplate 을 살펴보겠습니다 .

SSL이 구성되지 않은 경우 다음 테스트가 예상대로 실패합니다.

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

보시다시피 이것은 원시 HttpClient에 대해 SSL을 구성한 방식과 매우 유사합니다. SSL 지원으로 요청 팩터리를 구성한 다음 이 미리 구성된 팩터리를 전달하는 템플릿을 인스턴스화합니다.

6. SSL을 사용하는 Spring RestTemplate (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<String> response 
      = new RestTemplate(requestFactory).exchange(
      urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

7. 결론

이 사용방법(예제)에서는 인증서에 관계없이 모든 HTTPS URL을 사용할 수 있도록 Apache HttpClient에 대해 SSL을 구성하는 방법에 대해 설명했습니다. Spring RestTemplate 에 대한 동일한 구성 도 설명되어 있습니다.

그러나 이해해야 할 중요한 점은 이 전략이 인증서 검사 를 완전히 무시한다는 것입니다. 따라서 안전하지 않고 합당한 경우에만 사용됩니다.

이러한 예제의 구현은 GitHub 프로젝트 에서 찾을 수 있습니다. 이것은 Eclipse 기반 프로젝트이므로 그대로 가져오고 실행하기 쉬워야 합니다.

HTTPClient footer