Fidarsi di tutti i certificati con okHttp


111

A scopo di test, sto cercando di aggiungere una fabbrica di socket al mio client okHttp che si fida di tutto mentre è impostato un proxy. Questo è stato fatto molte volte, ma la mia implementazione di una fabbrica di socket affidabile sembra mancare qualcosa:

class TrustEveryoneManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }

    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }

    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
OkHttpClient client = new OkHttpClient();

final InetAddress ipAddress = InetAddress.getByName("XX.XXX.XXX.XXX"); // some IP
client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipAddress, 8888)));

SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[]{new TrustEveryoneManager()};
sslContext.init(null, trustManagers, null);
client.setSslSocketFactory(sslContext.getSocketFactory);

Nessuna richiesta viene inviata dalla mia app e nessuna eccezione viene registrata, quindi sembra che stia fallendo silenziosamente all'interno di okHttp. Dopo ulteriori indagini, sembra che ci sia un'eccezione che viene inghiottita in okHttp Connection.upgradeToTls()quando viene forzata la stretta di mano. L'eccezione che mi viene data è:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Il codice seguente produce un SSLContextche funziona come un fascino nella creazione di un SSLSocketFactory che non genera eccezioni:

protected SSLContext getTrustingSslContext() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
    final SSLContextBuilder trustingSSLContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true; // Accepts any ssl cert whether valid or not.
                }
            });
    return trustingSSLContextBuilder.build();
}

Il problema è che sto cercando di rimuovere completamente tutte le dipendenze di Apache HttpClient dalla mia app. Il codice sottostante con Apache HttpClient per produrre SSLContextsembra abbastanza semplice, ma ovviamente mi manca qualcosa perché non posso configurare il mio SSLContextin modo che corrisponda a questo.

Qualcuno sarebbe in grado di produrre un'implementazione di SSLContext che fa quello che vorrei senza usare Apache HttpClient?


2
"A scopo di test, sto cercando di aggiungere una fabbrica di socket al mio client okHttp che si fida di tutto" - cosa ti fa pensare che questa sia una buona idea in primo luogo?
CommonsWare

1
Viene chiamato solo su una rete di cui mi fido completamente.
mare al

Ciò causa unjava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Risposte:


250

Nel caso qualcuno cadesse qui, la (unica) soluzione che ha funzionato per me è stata creare il OkHttpClientsimile spiegato qui .

Ecco il codice:

private static OkHttpClient getUnsafeOkHttpClient() {
  try {
    // Create a trust manager that does not validate certificate chains
    final TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
          @Override
          public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
          }
        }
    };

    // Install the all-trusting trust manager
    final SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    // Create an ssl socket factory with our all-trusting manager
    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
    builder.hostnameVerifier(new HostnameVerifier() {
      @Override
      public boolean verify(String hostname, SSLSession session) {
        return true;
      }
    });

    OkHttpClient okHttpClient = builder.build();
    return okHttpClient;
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

15
Perché SSLe no TLS?
IgorGanapolsky

1
Grazie mille per questo! La documentazione sul retrofit (usa okhttp) manca, questo tipo di esempi di codice mi fa risparmiare così tanto tempo ancora e ancora.
Warpzit

8
Nota che questo approccio non funziona più con le versioni correnti di OkHttp. Con 3.1.1 sembra completamente rotto. Dalla 3.1.2 in poi, X509TrustManager.getAcceptedIssuers()deve restituire un array vuoto invece di null. Per ulteriori informazioni, vedere questo commit (scorrere verso il basso e vedere le note in RealTrustRootIndex.java).
jbxbergdev

12
Ho provato questo, ma ottengo ancora Handshake failedeccezioni. Eventuali suggerimenti?
Esteban


13

Il seguente metodo è deprecato

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Considera l'idea di aggiornarlo a

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)

13

Aggiorna OkHttp 3.0, la getAcceptedIssuers()funzione deve restituire un array vuoto invece di null.


2
@Override public X509Certificate [] getAcceptedIssuers () {return new X509Certificate [] {}; // StackOverflow}
Ervin Zhang

11

SSLSocketFactory non espone il suo X509TrustManager, che è un campo di cui OkHttp ha bisogno per costruire una catena di certificati pulita. Questo metodo invece deve utilizzare la riflessione per estrarre il gestore della fiducia. Le applicazioni dovrebbero preferire chiamare sslSocketFactory (SSLSocketFactory, X509TrustManager), che evita tale riflessione.

Fonte: documentazione OkHttp

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.sslSocketFactory(sslContext.getSocketFactory(),
    new X509TrustManager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
        }
    });

Questo non funziona per i certificati autofirmati. Causa un errore 401 non autorizzato .
IgorGanapolsky

5
Da dove sslContextviene?
Anonsage

3
Come si inizializza sslContext?
kiltek

10

Questa è la soluzione di sonxurxo a Kotlin, se qualcuno ne ha bisogno.

private fun getUnsafeOkHttpClient(): OkHttpClient {
    // Create a trust manager that does not validate certificate chains
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
    })

    // Install the all-trusting trust manager
    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, trustAllCerts, java.security.SecureRandom())
    // Create an ssl socket factory with our all-trusting manager
    val sslSocketFactory = sslContext.socketFactory

    return OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
        .hostnameVerifier { _, _ -> true }.build()
}


7

Ho creato una funzione di estensione per Kotlin. Incollalo dove vuoi e importalo durante la creazione OkHttpClient.

fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
    val naiveTrustManager = object : X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
    }

    val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
        val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
        init(null, trustAllCerts, SecureRandom())
    }.socketFactory

    sslSocketFactory(insecureSocketFactory, naiveTrustManager)
    hostnameVerifier(HostnameVerifier { _, _ -> true })
    return this
}

usalo in questo modo:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()

Puoi effettivamente usarlo in questo modo: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) ecc., Dopotutto è un modello di builder
Emanuel Moecklin

1

Questa è la soluzione Scala se qualcuno ne ha bisogno

def anUnsafeOkHttpClient(): OkHttpClient = {
val manager: TrustManager =
  new X509TrustManager() {
    override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def getAcceptedIssuers = Seq.empty[X509Certificate].toArray
  }
val trustAllCertificates =  Seq(manager).toArray

val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCertificates, new java.security.SecureRandom())
val sslSocketFactory = sslContext.getSocketFactory()
val okBuilder = new OkHttpClient.Builder()
okBuilder.sslSocketFactory(sslSocketFactory, trustAllCertificates(0).asInstanceOf[X509TrustManager])
okBuilder.hostnameVerifier(new NoopHostnameVerifier)
okBuilder.build()

}


-12

Non dovresti mai cercare di sovrascrivere la convalida del certificato nel codice! Se è necessario eseguire dei test, utilizzare una CA interna / di prova e installare il certificato radice della CA sul dispositivo o sull'emulatore. Puoi usare BurpSuite o Charles Proxy se non sai come configurare una CA.


37
Oh andiamo. E se lavori per un'azienda e ti costringono a connetterti al loro server HTTPS tramite VPN. Come lo simulerai in un test?
IgorGanapolsky

5
Sì, questo è il purissimo lavaggio del maiale. Lo compiliamo solo in build di test, quindi non c'è pericolo.
Adam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.