HttpClient con SSL

1. Panoramica

Questo articolo mostrerà come configurare Apache HttpClient 4 con il supporto SSL "Accetta tutto" . L'obiettivo è semplice: utilizzare URL HTTPS che non dispongono di certificati validi.

Se vuoi approfondire e imparare altre cose interessanti che puoi fare con HttpClient, vai alla guida principale di HttpClient .

2. SSLPeerUnverifiedException

Senza configurare SSL con HttpClient , il seguente test, che utilizza un URL HTTPS, avrà esito negativo:

public class RestClientLiveManualTest { @Test(expected = SSLPeerUnverifiedException.class) public void whenHttpsUrlIsConsumed_thenException() throws ClientProtocolException, IOException { CloseableHttpClient httpClient = HttpClients.createDefault(); String urlOverHttps = "//localhost:8082/httpclient-simple"; HttpGet getMethod = new HttpGet(urlOverHttps); HttpResponse response = httpClient.execute(getMethod); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } }

L'errore esatto è:

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) ...

L' eccezione javax.net.ssl.SSLPeerUnverifiedException si verifica ogni volta che non è stato possibile stabilire una catena di attendibilità valida per l'URL.

3. Configura SSL - Accetta tutto (HttpClient <4.3)

Configuriamo ora il client HTTP per considerare attendibili tutte le catene di certificati indipendentemente dalla loro validità:

@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)); }

Con il nuovo TrustStrategy che ora sovrascrive il processo di verifica del certificato standard (che dovrebbe consultare un gestore di fiducia configurato), il test ora passa e il client è in grado di utilizzare l'URL HTTPS .

4. Configura SSL - Accetta tutto (HttpClient 4.4 e versioni successive)

Con il nuovo HTTPClient, ora abbiamo un verificatore del nome host SSL predefinito migliorato e riprogettato. Inoltre, con l'introduzione di SSLConnectionSocketFactory e RegistryBuilder , è facile creare SSLSocketFactory. Quindi possiamo scrivere il test case sopra come:

@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. Spring RestTemplate con SSL (HttpClient <4.3)

Ora che abbiamo visto come configurare un HttpClient grezzo con supporto SSL, diamo un'occhiata a un client di livello superiore: lo Spring RestTemplate .

Senza SSL configurato, il seguente test fallisce come previsto:

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

Quindi configuriamo 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 = "//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)); }

Come puoi vedere, questo è molto simile al modo in cui abbiamo configurato SSL per il raw HttpClient : configuriamo la factory di richiesta con il supporto SSL e quindi istanziamo il modello che passa questa factory preconfigurata.

6. Spring RestTemplate con SSL (HttpClient 4.4)

E possiamo usare lo stesso modo per configurare il nostro 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. Conclusione

Questo tutorial ha discusso come configurare SSL per un Apache HttpClient in modo che sia in grado di utilizzare qualsiasi URL HTTPS, indipendentemente dal certificato. Viene illustrata anche la stessa configurazione per Spring RestTemplate .

Una cosa importante da capire, tuttavia, è che questa strategia ignora completamente il controllo dei certificati , il che lo rende insicuro e può essere utilizzato solo dove ha senso.

L'implementazione di questi esempi può essere trovata nel progetto GitHub: questo è un progetto basato su Eclipse, quindi dovrebbe essere facile da importare ed eseguire così com'è.