Faire confiance à tous les certificats avec okHttp

111

À des fins de test, j'essaie d'ajouter une fabrique de sockets à mon client okHttp qui fait confiance à tout pendant qu'un proxy est défini. Cela a été fait plusieurs fois, mais mon implémentation d'une fabrique de sockets de confiance semble manquer quelque chose:

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

Aucune demande n'est envoyée depuis mon application et aucune exception n'est enregistrée, il semble donc qu'elle échoue silencieusement dans okHttp. Après une enquête plus approfondie, il semble qu'une exception soit engloutie dans okHttp Connection.upgradeToTls()lorsque la poignée de main est forcée. L'exception que l'on me donne est:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Le code suivant produit un SSLContextqui fonctionne comme un charme dans la création d'un SSLSocketFactory qui ne lève aucune exception:

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

Le problème est que j'essaie de supprimer complètement toutes les dépendances Apache HttpClient de mon application. Le code sous-jacent avec Apache HttpClient pour produire le SSLContextsemble assez simple, mais il me manque évidemment quelque chose car je ne peux pas configurer mon SSLContextpour correspondre à cela.

Est-ce que n'importe qui pourrait produire une implémentation de SSLContext qui fait ce que je voudrais sans utiliser Apache HttpClient?

Seato
la source
2
«À des fins de test, j'essaie d'ajouter une fabrique de sockets à mon client okHttp qui fait confiance à tout» - qu'est-ce qui vous fait penser que c'est une bonne idée en premier lieu?
CommonsWare
1
Il n'est appelé que sur un réseau auquel je fais entièrement confiance.
seato
Cela provoque unjava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Réponses:

250

Juste au cas où quelqu'un tomberait ici, la (seule) solution qui a fonctionné pour moi est de créer ce qui est OkHttpClientexpliqué ici .

Voici le 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
la source
15
Pourquoi SSLet pas TLS?
IgorGanapolsky
1
Merci beaucoup pour cela! La documentation sur la modernisation (utilise okhttp) fait défaut, ce genre d'exemples de code me fait gagner beaucoup de temps encore et encore.
Warpzit
8
Notez que cette approche ne fonctionne plus avec les versions actuelles d'OkHttp. Avec 3.1.1, il semble complètement cassé. À partir de la version 3.1.2, X509TrustManager.getAcceptedIssuers()doit retourner un tableau vide au lieu de null. Pour plus d'informations, consultez ce commit (faites défiler vers le bas et consultez les notes sous RealTrustRootIndex.java).
jbxbergdev
12
J'ai essayé cela, mais je reçois toujours une Handshake failedexception. Aucune suggestion?
Esteban
13

La méthode suivante est obsolète

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Pensez à le mettre à jour en

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Jakub N
la source
13

Mettez à jour OkHttp 3.0, la getAcceptedIssuers()fonction doit renvoyer un tableau vide au lieu de null.

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

SSLSocketFactory n'expose pas son X509TrustManager, qui est un champ dont OkHttp a besoin pour créer une chaîne de certificats propre. Cette méthode doit à la place utiliser la réflexion pour extraire le gestionnaire de confiance. Les applications doivent préférer appeler sslSocketFactory (SSLSocketFactory, X509TrustManager), ce qui évite une telle réflexion.

Source: documentation 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[]{};
        }
    });
Tharindu Bandara
la source
Cela ne fonctionne pas pour les certificats auto-signés. Provoque une erreur 401 non autorisée .
IgorGanapolsky
5
D'où vient le sslContext?
Anonsage
3
Comment initialisez-vous sslContext?
kiltek
10

C'est la solution de sonxurxo à Kotlin, si quelqu'un en a besoin.

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
la source
7

J'ai créé une fonction d'extension pour Kotlin. Collez-le où vous le souhaitez et importez-le lors de la création 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
}

utilisez-le comme ceci:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()
George Shalvashvili
la source
Vous pouvez en fait l'utiliser comme ceci: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) etc., c'est un modèle de constructeur après tout
Emanuel Moecklin
1

C'est la solution Scala si quelqu'un en a besoin

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
la source
-12

Vous ne devriez jamais chercher à remplacer la validation de certificat dans le code! Si vous devez effectuer des tests, utilisez une autorité de certification interne / de test et installez le certificat racine de l'autorité de certification sur l'appareil ou l'émulateur. Vous pouvez utiliser BurpSuite ou Charles Proxy si vous ne savez pas comment configurer une autorité de certification.

Tony Trummer
la source
37
Oh allez. Et si vous travaillez pour une entreprise et qu'ils vous obligent à vous connecter à leur serveur HTTPS via VPN. Comment vas-tu simuler ça dans un test?
IgorGanapolsky
5
Ya c'est le plus pur lavage de porc. Nous ne compilons cela que dans les versions de test, il n'y a donc aucun danger.
Adam