okhttp: Unable to extract the trust manager

Hi, after upgrading from OkHttp 3.0.1 to 3.1.0 I get the following stack trace and crash after calling build() to create a OkHttpClient.

java.lang.IllegalStateException: Unable to extract the trust manager on okhttp3.internal.Platform$Android@1d8cf999, sslSocketFactory is class com.google.android.gms.org.conscrypt.KitKatPlatformOpenSSLSocketAdapterFactory
                                                                        at okhttp3.OkHttpClient.<init>(OkHttpClient.java:187)
                                                                        at okhttp3.OkHttpClient.<init>(OkHttpClient.java:60)
                                                                        at okhttp3.OkHttpClient$Builder.build(OkHttpClient.java:719)

This issue was not present in 3.0.1.

Here’s where the crash happens:

OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS);

            OkHttpClient client = builder.build();

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 72 (18 by maintainers)

Commits related to this issue

Most upvoted comments

I experienced this error while using a custom SocketFactory. I realized I didn’t add all the necessary ProGuard rules. I added the following to fix the issue:

-keepclassmembers class * implements javax.net.ssl.SSLSocketFactory {
    private final javax.net.ssl.SSLSocketFactory delegate;
}

In my SocketFactory class, I had the following field:

private final SSLSocketFactory delegate;

So adjust the ProGuard rule accordingly. E.g, the access modifiers like private, final, etc. as well as the field name (delegate) should match exactly as written in the class file.

Wildfly 10, java8, and any version > 3.0.1: Unable to extract the trust manager on okhttp3.internal.Platform@10c89bdf, sslSocketFactory is class sun.security.ssl.SSLSocketFactoryImpl

If you rename that factory’s private defaultFactory field to delegate, the hacky detector we use will do the right thing.

private SSLSocketFactory defaultFactory;

to

private SSLSocketFactory delegate;

Hey @swankjesse. I figured it would be a matter of time 😃

It turns out we actually do give our delegate factory the variable name “delegate”… but ProGuard changes it. It should be an easy fix. I believe we’ll just need to update our OkHttp regression tests to use the latest version.

For others that are watching, we’re still wrapping up some large changes to our networking monitoring code. We should be able to get to this in about 2-3 weeks.

For the Apteligent Android SDK, we’ve made some changes in version 5.6.2 that should address the issue. You can find the downloadable file for the version here:

https://app.crittercism.com/downloads/download/crittercism_v5_6_2_sdkonly.jar

+1 Having this same issue on retrofit:2.1.0, I’m simply trying to build an instance of retrofit on the setUp method of a test. I’m not event using roboelectric, I am using Mockito and PowerMock though

    testCompile 'org.mockito:mockito-core:1.10.19'
    testCompile 'org.powermock:powermock:1.6.5'
    testCompile 'org.powermock:powermock-module-junit4:1.6.5'
    testCompile 'org.powermock:powermock-api-mockito:1.6.5'
@Before public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BuildConfig.BASE_URL)
                .build(); //Crash here -> java.lang.AssertionError at okhttp3.OkHttpClient.systemDefaultTrustManager(OkHttpClient.java:260)

}

Full stacktrace:

java.lang.AssertionError
    at okhttp3.OkHttpClient.systemDefaultTrustManager(OkHttpClient.java:260)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.java:228)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.java:203)
    at retrofit2.Retrofit$Builder.build(Retrofit.java:551)
    at com.productify.urge.usecase.GetCategoriesUseCaseTest.setUp(GetCategoriesUseCaseTest.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:133)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:96)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:300)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:288)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:208)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:121)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:123)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

The 2nd problem is currently if you want to pass an SSL session cache, you have to pass in a SSL Socket factory:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(SSLCertificateSocketFactory.getDefault(socketOperationTimeout, sslSessionCache));

But even for stock Android SSLCertificateSocketFactory relies on com.android.org.conscrypt.OpenSSLContextImpl so the trust manager code fails looking for sun.security.ssl.SSLContextImpl.

It turns out the Parse Android SDK, which includes OkHttp2, can’t be upgraded until we get to 3.3.0. I attempted to do so just now and ran into the issue here. https://github.com/ParsePlatform/Parse-SDK-Android/pull/435/files#r58793351

I can get around the issue by using the snapshot version and implementing the code to get the default trust manager:


  private X509TrustManager systemDefaultTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
              TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
                + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

And then later:

builder.sslSocketFactory(SSLCertificateSocketFactory.getDefault(socketOperationTimeout, sslSessionCache), systemDefaultTrustManager());

So waiting for the 3.3.0 release before Parse’s Android SDK implementation can be bumped to use OkHttp3. 😃

@swankjesse thanks for the suggestion! I was already using reflection to set certain fields so the internal reflection would not run (kind of ironic if you think about it)

Edit: tested, and it works, thanks! Current solution looks like this:

    @Provides
    public X509TrustManager provideX509TrustManager() {
        try {
            TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            factory.init((KeyStore) null);
            TrustManager[] trustManagers = factory.getTrustManagers();
            return (X509TrustManager) trustManagers[0];
        } catch (NoSuchAlgorithmException | KeyStoreException exception) {
            Log.e(getClass().getSimpleName(), "not trust manager available", exception);
        }

        return null;
    }

    @Provides
    public SSLSocketFactory provideSSLSocketFactory(X509TrustManager trustManager) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, null);
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException exception) {
            Log.e(getClass().getSimpleName(), "not tls ssl socket factory available", exception);
        }

        return (SSLSocketFactory) SSLSocketFactory.getDefault();
    }

    @Provides
    public OkHttpClient provideHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory, trustManager)
                .build();
    }

Can you please elaborate on your above comment a little bit more @swankjesse !! That would be very helpful.