"no such provider: BCJSSE" when using Robolectric after upgrading to OkHttp 4.7.0 or later

Viewed 288

I have some tests which use OkHttp to verify some server call results and are run using the RobolectricTestRunner.

After upgrading the OkHttp library from version 4.6.0 to anything above it, I am unable to instantiate an OkHttpClient, which fails with the following error:

java.security.NoSuchProviderException: no such provider: BCJSSE

I have read through OkHttp's Changelog but cannot find any mention of changes related to Bouncycastle, which I suspect has to do with the crash, or anything else that seems related to this issue.

When running the test as a Junit4 test, instead of the RobolectricTestRunner, everything works fine, but I rely on robolectric providing functionality for my actual tests that cannot easily be mocked, so I have to keep using it.

I am using

  • Robolectric 4.3.1
  • Android Studio 4.
  • Android Gradle Plugin 3.5.0

What is the reason for the crash when updating the OkHttp dependency? And what can be done to solve this issue?

Smallest code snippet to replicate the issue:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

import okhttp3.OkHttpClient;

@RunWith( RobolectricTestRunner.class )
public class TestClass {

  @Test
  public void test() {
    OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build();

    //do the actual teststuff
  }
}

Stacktrace:

java.security.NoSuchProviderException: no such provider: BCJSSE

    at sun.security.jca.GetInstance.getService(GetInstance.java:83)
    at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
    at javax.net.ssl.TrustManagerFactory.getInstance(TrustManagerFactory.java:183)
    at okhttp3.internal.platform.BouncyCastlePlatform.platformTrustManager(BouncyCastlePlatform.kt:41)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:237)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:222)
    at path.to.my.class.TestClass.test(TestClass.java:12)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
    at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
    at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
1 Answers

OkHttp uses the registration of security providers to tell us which Platform instance to use. This code has changed recently because of incompatible changes in both Android and Android Studio.

Looking into Robolectric code, they are registering only one of the two necessary providers.

https://github.com/robolectric/robolectric/blob/96ff4116ea83e93339d295e98175a8e49e2aadbc/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java#L143-L145

if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
  Security.insertProviderAt(new BouncyCastleProvider(), 1);
}

You should be able to workaround this by registering both yourself.

    Security.insertProviderAt(BouncyCastleProvider(), 1)
    Security.insertProviderAt(BouncyCastleJsseProvider(), 2)
Related