Vertrauen aller Zertifikate mit okHttp

111

Zu Testzwecken versuche ich, meinem okHttp-Client eine Socket-Factory hinzuzufügen, die alles vertraut, während ein Proxy festgelegt ist. Dies wurde schon oft getan, aber meiner Implementierung einer vertrauenswürdigen Socket-Factory scheint etwas zu fehlen:

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

Es werden keine Anfragen von meiner App gesendet und es werden keine Ausnahmen protokolliert. Es scheint also, dass dies in okHttp stillschweigend fehlschlägt. Bei weiteren Untersuchungen scheint es, dass eine Ausnahme in okHttp verschluckt wird, Connection.upgradeToTls()wenn der Handschlag erzwungen wird. Die Ausnahme, die mir gegeben wird, ist:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Der folgende Code erzeugt eine, SSLContextdie beim Erstellen einer SSLSocketFactory, die keine Ausnahmen auslöst, wie ein Zauber wirkt:

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

Das Problem ist, dass ich versuche, alle Apache HttpClient-Abhängigkeiten vollständig aus meiner App zu entfernen. Der zugrunde liegende Code mit Apache HttpClient zur Erstellung des Codes SSLContextscheint recht einfach zu sein, aber mir fehlt offensichtlich etwas, da ich meinen nicht konfigurieren kannSSLContext , dass er diesem entspricht.

Wäre jemand in der Lage, eine SSLContext-Implementierung zu erstellen, die das tut, was ich möchte, ohne Apache HttpClient zu verwenden?

Seato
quelle
2
"Zu Testzwecken versuche ich, meinem okHttp-Client eine Socket-Factory hinzuzufügen, die allem vertraut" - warum denken Sie, dass dies überhaupt eine gute Idee ist?
CommonsWare
1
Es wird nur in einem Netzwerk aufgerufen, dem ich voll und ganz vertraue.
Seato
Dies verursacht einejava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Antworten:

249

Für den Fall, dass jemand hierher fällt, besteht die (einzige) Lösung, die für mich funktioniert hat, darin, das hierOkHttpClient erläuterte zu erstellen .

Hier ist der Code:

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);
  }
}
sonxurxo
quelle
15
Warum SSLund nicht TLS?
IgorGanapolsky
1
Vielen Dank dafür! Die Dokumentation zur Nachrüstung (verwendet okhttp) fehlt, diese Art von Codebeispielen spart mir immer wieder so viel Zeit.
Warpzit
8
Beachten Sie, dass dieser Ansatz mit aktuellen Versionen von OkHttp nicht mehr funktioniert. Mit 3.1.1 scheint es völlig kaputt zu sein. Ab 3.1.2 X509TrustManager.getAcceptedIssuers()muss anstelle von ein leeres Array zurückgegeben werden null. Weitere Informationen finden Sie in diesem Commit (scrollen Sie nach unten und lesen Sie die Hinweise unter RealTrustRootIndex.java).
jbxbergdev
12
Ich habe es versucht, aber ich bekomme immer noch eine Handshake failedAusnahme. Irgendwelche Vorschläge?
Esteban
1
Beispiel von OkHttpClient: github.com/square/okhttp/blob/master/samples/guide/src/main/…
ilyamuromets
13

Die folgende Methode ist veraltet

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Erwägen Sie eine Aktualisierung auf

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Jakub N.
quelle
13

Aktualisieren Sie OkHttp 3.0, die getAcceptedIssuers()Funktion muss stattdessen ein leeres Array zurückgebennull .

Ervin Zhang
quelle
2
@Override public X509Certificate [] getAcceptedIssuers () {neues X509Certificate zurückgeben [] {}; // StackOverflow}
Ervin Zhang
11

SSLSocketFactory macht seinen X509TrustManager nicht verfügbar. Dies ist ein Feld, das OkHttp zum Erstellen einer sauberen Zertifikatkette benötigt. Diese Methode muss stattdessen Reflection verwenden, um den Vertrauensmanager zu extrahieren. Anwendungen sollten lieber sslSocketFactory (SSLSocketFactory, X509TrustManager) aufrufen, um solche Überlegungen zu vermeiden.

Quelle: OkHttp-Dokumentation

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[]{};
        }
    });
Tharindu Bandara
quelle
Dies funktioniert nicht für selbstsignierte Zertifikate. Verursacht einen 401 Unauthorized- Fehler.
IgorGanapolsky
5
Woher kommt das sslContext?
Anonsage
3
Wie initialisierst du sslContext?
Kiltek
9

Dies ist die Lösung von sonxurxo in Kotlin, falls jemand sie benötigt.

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()
}
Doodla
quelle
7

Ich habe eine Erweiterungsfunktion für Kotlin erstellt. Fügen Sie es ein, wo immer Sie möchten, und importieren Sie es beim Erstellen 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
}

benutze es so:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()
George Shalvashvili
quelle
Sie können es tatsächlich so verwenden: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) usw., es ist schließlich ein Builder-Muster
Emanuel Moecklin
1

Dies ist die Scala-Lösung, wenn jemand sie benötigt

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

}}

Netta
quelle
-12

Sie sollten niemals versuchen, die Zertifikatvalidierung im Code zu überschreiben! Wenn Sie Tests durchführen müssen, verwenden Sie eine interne CA / Test-CA und installieren Sie das CA-Stammzertifikat auf dem Gerät oder Emulator. Sie können BurpSuite oder Charles Proxy verwenden, wenn Sie nicht wissen, wie Sie eine Zertifizierungsstelle einrichten.

Tony Trummer
quelle
37
Ach komm schon. Was ist, wenn Sie für ein Unternehmen arbeiten und Sie gezwungen sind, über VPN eine Verbindung zu ihrem HTTPS-Server herzustellen? Wie wirst du das in einem Test simulieren?
IgorGanapolsky
5
Ya das ist reinste Schweinwäsche. Wir kompilieren dies nur in Testbuilds, daher besteht keine Gefahr.
Adam