web-dev-qa-db-fra.com

Faire confiance à tous les certificats avec okHttp

À des fins de test, j'essaie d'ajouter une fabrique de sockets à mon client okHttp qui approuve tout lorsqu'un proxy est défini. Cela a été fait à maintes reprises, mais il manque quelque chose dans mon implémentation d'une fabrique de sockets de confiance:

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'a été envoyée à partir de mon application et aucune exception n'a été consignée. Il semble donc que cela échoue silencieusement dans okHttp. Après une enquête plus approfondie, il semble qu’une exception soit avalée dans la Connection.upgradeToTls() de okHttp lorsque la poignée de main est forcée. L'exception que je reçois 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 SSLContext qui fonctionne comme un charme pour créer 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 SSLContext semble assez simple, mais il me manque évidemment quelque chose car je ne peux pas configurer mon SSLContext pour correspondre à cela.

Est-ce que quelqu'un serait capable de produire une implémentation SSLContext qui ferait ce que je voudrais sans utiliser Apache HttpClient?

92
seato

Juste au cas où quelqu'un tomberait ici, la (seule) solution qui a fonctionné pour moi est la création de la OkHttpClient comme expliqué 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);
  }
}
212
sonxurxo

La méthode suivante est obsolète

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Pensez à le mettre à jour pour

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
13
Jakub N

SSLSocketFactory n'expose pas son X509TrustManager, qui est un champ dont OkHttp a besoin pour créer une chaîne de certificats vierge. Cette méthode doit 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[]{};
        }
    });
11
Tharindu Bandara

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

11
Ervin Zhang

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

3
doodla

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

}

0
netta