150

For testing purposes, I'm trying to add a socket factory to my okHttp client that trusts everything while a proxy is set. This has been done many times over, but my implementation of a trusting socket factory seems to be missing something:

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

No requests are being sent out of my app and no exceptions are getting logged so it seems that it's failing silently within okHttp. Upon further investigation, it seems that there is an Exception being swallowed up in okHttp's Connection.upgradeToTls() when the handshake is being forced. The exception I'm being given is: javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

The following code produces an SSLContext which works like a charm in creating an SSLSocketFactory that doesn't throw any exceptions:

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

The issue is that I'm trying to remove all Apache HttpClient dependencies from my app completely. The underlying code with Apache HttpClient to produce the SSLContext seems straightforward enough, but I'm obviously missing something as I cannot configure my SSLContext to match this.

Would anyone be able to produce an SSLContext implementation which does what I'd like without using Apache HttpClient?

ok2c
  • 26,450
  • 5
  • 63
  • 71
seato
  • 2,061
  • 2
  • 15
  • 18

8 Answers8

315

Just in case anyone falls here, the (only) solution that worked for me is creating the OkHttpClient like explained here.

Here is the 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);
  }
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
sonxurxo
  • 5,648
  • 2
  • 23
  • 33
  • 21
    Why `SSL` and not `TLS`? – IgorGanapolsky Oct 23 '15 at 16:42
  • 1
    Thanks a lot for this! The documentation on retrofit (uses okhttp) is lacking, this kind of code samples saves me so much time again and again. – Warpzit Jan 07 '16 at 14:31
  • 8
    Note this approach doesn't work any more with current versions of OkHttp . With 3.1.1 it seems completely broken. From 3.1.2 onwards, `X509TrustManager.getAcceptedIssuers()` must return an empty array instead of `null`. For more information, see [this commit](https://github.com/square/okhttp/commit/784fabac7d1586a5614bd4bc8854fd62850dbe26#commitcomment-15958615) (scroll down and see the notes under RealTrustRootIndex.java). – jbxbergdev Feb 10 '16 at 17:05
  • I have tried this with Retrofit2 and OkHttp3 but it not work, Why ? – François Legrand Feb 06 '17 at 14:12
  • 17
    I've tried this, but I still get `Handshake failed` exception. Any suggestions? – Esteban Apr 20 '17 at 09:06
  • Okhttp3. The testing goes into a dead loop without any messages. – halxinate Aug 24 '17 at 23:14
  • 3
    example from OkHttpClient: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java – ilyamuromets Oct 23 '17 at 15:45
  • Just for you to know: this stuff should not be used if your app is collecting any sensitive user data. Trusting everything can lead you to a situation when user data is being sent somewhere for evil purposes. – RexSplode Jan 12 '18 at 11:27
  • Compile error is there - builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]); Line will not compile in android, you have to remove second parameter (X509TrustManager)trustAllCerts[0] from this method call. – Dinakar Prasad Maurya Feb 05 '18 at 06:07
  • Why do I get a **401 Unauthorized** error with this code? – IgorGanapolsky Jun 05 '18 at 14:54
  • what about HostnameVerifier hv = new TrustAllHostnameVerifier(); – eyurdakul Oct 04 '18 at 12:29
  • You can use new NoopHostnameVerifier() instead of creating your own HostnameVerifier – netta Aug 01 '19 at 08:41
  • The provided above solution work for me. But still, I need to write the whole code in java and then call in Kotlin class (as kotlin android project). Got stuck with the line " return new java.security.cert.X509Certificate[]{}; " as the need to transform that to kotlin equivalent " return arrayOf(X509Certificate)" but showing error can't instantiate an abstract class. – blackjack Sep 12 '19 at 06:28
  • It does not work at first, but downgrading okhttp to 3.1.2 solves the problem. – steven Dec 09 '19 at 07:33
  • After cracking my head with different solutions for letting OkHttpClient accept the trustStore and trustStorePassword, this unSafeSolution actually worked!! Thanks @MarcoScavo – KNDheeraj Jan 10 '20 at 09:08
  • This solution doesn't cover the case when target host has a certificate in its certificate chain that used a singing algorithm, which was listed under `jdk.certpath.disabledAlgorithms` property in `java.security` file. For example, if `jdk.certpath.disabledAlgorithms` has `SHA1` and the host uses a certificate which was signed by CA using `SHA1withRSA` algorithm, HTTP request will fail with "java.security.cert.CertificateException: Certificates does not conform to algorithm constraints". To fix it, custom `X509ExtendedTrustManager` needs to be used in the same way instead of `X509TrustManager` – ouid Oct 12 '22 at 11:21
  • I'm also getting 401 Unauthorized after implementing this. – Tejas Pandya Nov 10 '22 at 08:55
41

I made an extension function for Kotlin. Paste it where ever you like and import it while creating 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
}

use it like this:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()
George Shalvashvili
  • 1,263
  • 13
  • 21
23

This is sonxurxo's solution in Kotlin, if anyone needs it.

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
  • 413
  • 5
  • 17
16

Update OkHttp 3.0, the getAcceptedIssuers() function must return an empty array instead of null.

JJD
  • 50,076
  • 60
  • 203
  • 339
Ervin Zhang
  • 191
  • 1
  • 8
15

Following method is deprecated

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Consider updating it to

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Kuba
  • 372
  • 4
  • 8
  • i got `Unable to extract the trust manager on Android10Platform` in Android10, after providing `trustManaget` parameter it worked – Rukmal Dias Oct 20 '20 at 10:41
13

SSLSocketFactory does not expose its X509TrustManager, which is a field that OkHttp needs to build a clean certificate chain. This method instead must use reflection to extract the trust manager. Applications should prefer to call sslSocketFactory(SSLSocketFactory, X509TrustManager), which avoids such reflection.

Source: OkHttp documentation

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[]{};
        }
    });
JJD
  • 50,076
  • 60
  • 203
  • 339
4

This is the Scala solution if anyone needs it

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

}

JJD
  • 50,076
  • 60
  • 203
  • 339
netta
  • 508
  • 6
  • 16
-16

You should never look to override certificate validation in code! If you need to do testing, use an internal/test CA and install the CA root certificate on the device or emulator. You can use BurpSuite or Charles Proxy if you don't know how to setup a CA.

  • 42
    Oh come on. What if you are working for a company and they are forcing you to connect to their HTTPS server via VPN. How you gonna simulate that in a test? – IgorGanapolsky Oct 23 '15 at 16:46
  • 6
    Ya this is purest hog-wash. We only compile this in in test builds, so there is no danger. – Adam Mar 01 '16 at 20:04