Ignorare il certificato SSL in Apache HttpClient 4.3

Come ignorare il certificato SSL (attendibile tutto) per Apache HttpClient 4.3 ?

Tutte le risposte che ho trovato su SO trattano le versioni precedenti e l’API è cambiata.

Relazionato:

  • Come ignorare gli errori del certificato SSL in Apache HttpClient 4.0
  • Come gestire i certificati SSL non validi con Apache HttpClient?
  • È necessario fidarsi di tutti i certificati durante lo sviluppo utilizzando Spring
  • Ignora gli errori del certificato SSL con Java

Modificare:

  • È solo a scopo di test. Ragazzi, non provatelo a casa (o in produzione)

Il seguente codice funziona per fidarsi dei certificati autofirmati. Devi usare TrustSelfSignedStrategy quando crei il tuo cliente:

SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpGet httpGet = new HttpGet("https://some-server"); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } 

Non ho inserito SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER : il punto era consentire il test con certificati autofirmati in modo da non dover acquisire un certificato adeguato da un’autorità di certificazione. È ansible creare facilmente un certificato autofirmato con il nome host corretto, quindi farlo invece di aggiungere il flag SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER .

Se si sta utilizzando la procedura PoolingHttpClientConnectionManager sopra, non funziona, SSLContext personalizzato viene ignorato. È necessario passare socketFactoryRegistry in contructor durante la creazione di PoolingHttpClientConnectionManager.

 SSLContextBuilder builder = SSLContexts.custom(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLContext sslContext = builder.build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext, new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException { } @Override public void verify(String host, X509Certificate cert) throws SSLException { } @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { } @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); Registry socketFactoryRegistry = RegistryBuilder . create().register("https", sslsf) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager( socketFactoryRegistry); CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm).build(); 

In aggiunta alla risposta di @mavroprovato, se vuoi fidarti di tutti i certificati invece di solo autofirmati, dovresti farlo (nello stile del tuo codice)

 builder.loadTrustMaterial(null, new TrustStrategy(){ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); 

oppure (copia-incolla diretta dal mio codice):

 import javax.net.ssl.SSLContext; import org.apache.http.ssl.TrustStrategy; import org.apache.http.ssl.SSLContexts; // ... SSLContext sslContext = SSLContexts .custom() //FIXME to contain real trust store .loadTrustMaterial(new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }) .build(); 

E se vuoi saltare anche la verifica del nome host, devi impostare

  CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).setSSLHostnameVerifier( NoopHostnameVerifier.INSTANCE).build(); 

anche. (ALLOW_ALL_HOSTNAME_VERIFIER è obsoleto).

Avvertenza obbligatoria: non dovresti farlo, accettare tutti i certificati è una brutta cosa. Tuttavia ci sono alcuni casi rari di utilizzo in cui si desidera eseguire questa operazione.

Come nota per il codice precedentemente fornito, ti consigliamo di chiudere la risposta anche se httpclient.execute () genera un’eccezione

 CloseableHttpResponse response = null; try { response = httpclient.execute(httpGet); System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { if (response != null) { response.close(); } } 

Il codice sopra è stato testato usando

  org.apache.httpcomponents httpclient 4.5.3  

E per gli interessati, ecco il mio set di prova completo:

 import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; import org.junit.Test; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class TrustAllCertificatesTest { final String expiredCertSite = "https://expired.badssl.com/"; final String selfSignedCertSite = "https://self-signed.badssl.com/"; final String wrongHostCertSite = "https://wrong.host.badssl.com/"; static final TrustStrategy trustSelfSignedStrategy = new TrustSelfSignedStrategy(); static final TrustStrategy trustAllStrategy = new TrustStrategy(){ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }; @Test public void testSelfSignedOnSelfSignedUsingCode() throws Exception { doGet(selfSignedCertSite, trustSelfSignedStrategy); } @Test(expected = SSLHandshakeException.class) public void testExpiredOnSelfSignedUsingCode() throws Exception { doGet(expiredCertSite, trustSelfSignedStrategy); } @Test(expected = SSLPeerUnverifiedException.class) public void testWrongHostOnSelfSignedUsingCode() throws Exception { doGet(wrongHostCertSite, trustSelfSignedStrategy); } @Test public void testSelfSignedOnTrustAllUsingCode() throws Exception { doGet(selfSignedCertSite, trustAllStrategy); } @Test public void testExpiredOnTrustAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy); } @Test(expected = SSLPeerUnverifiedException.class) public void testWrongHostOnTrustAllUsingCode() throws Exception { doGet(wrongHostCertSite, trustAllStrategy); } @Test public void testSelfSignedOnAllowAllUsingCode() throws Exception { doGet(selfSignedCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } @Test public void testExpiredOnAllowAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } @Test public void testWrongHostOnAllowAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } public void doGet(String url, TrustStrategy trustStrategy, HostnameVerifier hostnameVerifier) throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(trustStrategy); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).setSSLHostnameVerifier(hostnameVerifier).build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } } public void doGet(String url, TrustStrategy trustStrategy) throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(trustStrategy); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } } } 

(progetto di test di lavoro in github )

Una piccola aggiunta alla risposta di vasekt:

La soluzione fornita con SocketFactoryRegistry funziona quando si utilizza PoolingHttpClientConnectionManager.

Tuttavia, le connessioni via http non funzionano più allora. È necessario aggiungere un PlainConnectionSocketFactory per il protocollo http per renderli nuovamente operativi:

 Registry socketFactoryRegistry = RegistryBuilder. create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()).build(); 

Dopo aver provato varie opzioni, la seguente configurazione ha funzionato sia per http che per https

  SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); Registry registry = RegistryBuilder.create() .register("http", new PlainConnectionSocketFactory()) .register("https", sslsf) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(2000);//max connection //System.setProperty("jsse.enableSNIExtension", "false"); //"" CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(sslsf) .setConnectionManager(cm) .build(); 

Sto usando http-client 4.3.3 –

compile 'org.apache.httpcomponents:httpclient:4.3.3'

Codice di lavoro più semplice e più breve:

Stiamo usando HTTPClient 4.3.5 e abbiamo provato quasi tutte le soluzioni nello stackoverflow ma niente, Dopo aver pensato e capito il problema, arriviamo al seguente codice che funziona perfettamente, basta aggiungerlo prima di creare l’istanza di HttpClient.

un metodo che usi per fare una richiesta di posta …

 SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(builder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSF).build(); HttpPost postRequest = new HttpPost(url); 

continuare a chiamare e utilizzare l’istanza di HttpPost nel formato normale

Quando si utilizza il client http 4.5 ho dovuto usare javasx.net.ssl.HostnameVerifier per consentire qualsiasi nome host (a scopo di test). Ecco cosa ho finito per fare:

 CloseableHttpClient httpClient = null; try { SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); HostnameVerifier hostnameVerifierAllowAll = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), hostnameVerifierAllowAll); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new AuthScope("192.168.30.34", 8443), new UsernamePasswordCredentials("root", "password")); httpClient = HttpClients.custom() .setSSLSocketFactory(sslSocketFactory) .setDefaultCredentialsProvider(credsProvider) .build(); HttpGet httpGet = new HttpGet("https://192.168.30.34:8443/axis/services/getStuff?firstResult=0&maxResults=1000"); CloseableHttpResponse response = httpClient.execute(httpGet); int httpStatus = response.getStatusLine().getStatusCode(); if (httpStatus >= 200 && httpStatus < 300) { [...] } else { throw new ClientProtocolException("Unexpected response status: " + httpStatus); } } catch (Exception ex) { ex.printStackTrace(); } finally { try { httpClient.close(); } catch (IOException ex) { logger.error("Error while closing the HTTP client: ", ex); } } 

Ecco una distillazione funzionante delle tecniche di cui sopra, equivalente a “curl –insecure”:

 HttpClient getInsecureHttpClient() throws GeneralSecurityException { TrustStrategy trustStrategy = new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) { return true; } }; HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; return HttpClients.custom() .setSSLSocketFactory(new SSLConnectionSocketFactory( new SSLContextBuilder().loadTrustMaterial(trustStrategy).build(), hostnameVerifier)) .build(); } 

Sopra PoolingHttpClientConnectionManager insieme a Registry socketFactoryRegistry = RegistryBuilder. create().register("https", sslFactory).build(); Se vuoi un httpclient asincrono usando PoolingNHttpClientConnectionManager il codice shoudl sarà simile al seguente

 SSLContextBuilder builder = SSLContexts.custom(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLContext sslContext = builder.build(); SchemeIOSessionStrategy sslioSessionStrategy = new SSLIOSessionStrategy(sslContext, new HostnameVerifier(){ @Override public boolean verify(String hostname, SSLSession session) { return true;// TODO as of now allow all hostnames } }); Registry sslioSessionRegistry = RegistryBuilder.create().register("https", sslioSessionStrategy).build(); PoolingNHttpClientConnectionManager ncm = new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(),sslioSessionRegistry); CloseableHttpAsyncClient asyncHttpClient = HttpAsyncClients.custom().setConnectionManager(ncm).build(); asyncHttpClient.start(); 
 class ApacheHttpClient { /*** * This is a https get request that bypasses certificate checking and hostname verifier. * It uses basis authentication method. * It is tested with Apache httpclient-4.4. * It dumps the contents of a https page on the console output. * It is very similar to http get request, but with the additional customization of * - credential provider, and * - SSLConnectionSocketFactory to bypass certification checking and hostname verifier. * @param path String * @param username String * @param password String * @throws IOException */ public void get(String path, String username, String password) throws IOException { final CloseableHttpClient httpClient = HttpClients.custom() .setDefaultCredentialsProvider(createCredsProvider(username, password)) .setSSLSocketFactory(createGenerousSSLSocketFactory()) .build(); final CloseableHttpResponse response = httpClient.execute(new HttpGet(path)); try { HttpEntity entity = response.getEntity(); if (entity == null) return; System.out.println(EntityUtils.toString(entity)); } finally { response.close(); httpClient.close(); } } private CredentialsProvider createCredsProvider(String username, String password) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password)); return credsProvider; } /*** * * @return SSLConnectionSocketFactory that bypass certificate check and bypass HostnameVerifier */ private SSLConnectionSocketFactory createGenerousSSLSocketFactory() { SSLContext sslContext; try { sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, new TrustManager[]{createGenerousTrustManager()}, new SecureRandom()); } catch (KeyManagementException | NoSuchAlgorithmException e) { e.printStackTrace(); return null; } return new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); } private X509TrustManager createGenerousTrustManager() { return new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] cert, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] cert, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; } } 

(Avrei aggiunto un commento direttamente alla risposta di vasekt ma non ho abbastanza punti reputazione (non sono sicuro della logica lì)

Ad ogni modo … quello che volevo dire è che anche se non stai creando / chiedendo esplicitamente una PoolingConnection, non significa che non ne stai ricevendo uno.

Stavo diventando pazzo cercando di capire perché la soluzione originale non ha funzionato per me, ma ho ignorato la risposta di vasekt in quanto “non si applicava al mio caso” – sbagliato!

Stavo fissando il mio stack-trace quando era basso e ho visto una PoolingConnection nel mezzo di esso. Bang – ho stanco la sua aggiunta e il successo !! (la nostra demo è domani e stavo diventando disperata) 🙂

Fidati di tutti i certificati nel client HTTP Apache

 TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sc); httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 

È ansible utilizzare il seguente snippet di codice per ottenere l’istanza HttpClient senza il controllo della certificazione SSL.

 private HttpClient getSSLHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { LogLoader.serverLog.trace("In getSSLHttpClient()"); SSLContext context = SSLContext.getInstance("SSL"); TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; context.init(null, new TrustManager[] { tm }, null); HttpClientBuilder builder = HttpClientBuilder.create(); SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(context); builder.setSSLSocketFactory(sslConnectionFactory); PlainConnectionSocketFactory plainConnectionSocketFactory = new PlainConnectionSocketFactory(); Registry registry = RegistryBuilder.create() .register("https", sslConnectionFactory).register("http", plainConnectionSocketFactory).build(); PoolingHttpClientConnectionManager ccm = new PoolingHttpClientConnectionManager(registry); ccm.setMaxTotal(BaseConstant.CONNECTION_POOL_SIZE); ccm.setDefaultMaxPerRoute(BaseConstant.CONNECTION_POOL_SIZE); builder.setConnectionManager((HttpClientConnectionManager) ccm); builder.disableRedirectHandling(); LogLoader.serverLog.trace("Out getSSLHttpClient()"); return builder.build(); } 

Se si utilizza HttpClient 4.5.x , il codice può essere simile al seguente:

 SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, TrustSelfSignedStrategy.INSTANCE).build(); SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, NoopHostnameVerifier.INSTANCE); HttpClient httpClient = HttpClients.custom() .setDefaultCookieStore(new BasicCookieStore()) .setSSLSocketFactory(sslSocketFactory) .build();