TLDR
, чтобы обойти эту проблему, установите системное свойство javax.net.ssl.trustStoreType
на JKS
:
-Djavax.net.ssl.trustStoreType=JKS
Подробности
Я нашел этот обходной путь :
Попробовав много вещей, я наконец нашел один обходной путь:
System.setProperty("javax.net.ssl.trustStore", "NONE")
MockWebServer()
Тесты проходят с этим дополнительная конфигурация.
Однако, когда я попробовал этот обходной путь, все мои HTTP-вызовы потерпели неудачу со следующим ConnectException
вместо:
java.net.ConnectException: Failed to connect to myhost.com:443
at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:265)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:183)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:88)
at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
at okhttp3.RealCall.execute(RealCall.java:81)
Я подозреваю, что OkHttp
правильно запрещает все соединения TLS, потому что не доверяет им, так как обходной путь предлагает установить javax.net.ssl.trustStore
на NONE
.
Я также попытался указать пароль по умолчанию для java доверенных хранилищ, changeit
через этот обходной путь :
обходной путь: -Djavax.net.ssl.trustStorePassword=changeit
Но затем я получил следующее исключение при попытке создать экземпляр OkHttpClient
:
java.lang.AssertionError: No System TLS
at okhttp3.internal.Util.platformTrustManager(Util.java:648)
at okhttp3.OkHttpClient.<init>(OkHttpClient.java:228)
at okhttp3.OkHttpClient.<init>(OkHttpClient.java:202)
, который был вызван:
Caused by: java.security.KeyStoreException: problem accessing trust store
at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:75)
at java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:278)
at okhttp3.internal.Util.platformTrustManager(Util.java:640)
... 22 more
Caused by: java.io.IOException: stream does not represent a PKCS12 key store
at org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.engineLoad(Unknown Source)
at java.base/java.security.KeyStore.load(KeyStore.java:1479)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:367)
at java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:315)
at java.base/sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:59)
at java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:51)
... 24 more
Это была отличная подсказка. Я начал отлаживать исходный код JDK 9 с помощью Android Studio и заметил, что когда я работал с org.junit.runners.BlockJUnit4ClassRunner
, мой KeyStore.keystoreSpi
был sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12
, но когда я работал с org.robolectric.RobolectricTestRunner
, мой KeyStore.keystoreSpi
было org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi$BCPKCS12KeyStore
.
Согласно JEP-229
:
Эта функция изменяет тип хранилища ключей по умолчанию с JKS для PKCS12. По умолчанию новые хранилища ключей создаются в формате хранилища ключей PKCS12. Существующие хранилища ключей не изменятся, и приложения хранилища ключей могут продолжать явно указывать требуемый тип хранилища ключей.
Существующие приложения не должны прерываться. Хранилища ключей, как правило, долговечны, поэтому нам необходимо поддерживать доступ к нескольким выпускам JDK. Приложения, которые обращаются к хранилищам ключей, созданным в более ранних выпусках JDK, должны работать без изменений в JDK 9. Аналогично, приложения, которые обращаются к хранилищам ключей, созданным в JDK 9, должны работать без изменений в более ранних выпусках JDK.
Это требование достигается путем введения механизма обнаружения хранилища ключей который понимает форматы JKS и PKCS12. Формат хранилища ключей проверяется перед его загрузкой, чтобы определить его тип, а затем для доступа к нему используется соответствующая реализация хранилища ключей. Механизм включен по умолчанию, но при необходимости его можно отключить.
Поддержка этого механизма обнаружения хранилища ключей может быть перенесена в более ранние выпуски JDK.
Таким образом, classi c $JAVA_HOME/lib/security/cacerts
по-прежнему является Java хранилищем ключей, которое я мог проверить:
$ file /Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/lib/security/cacerts
/Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/lib/security/cacerts: Java KeyStore
Однако, поскольку JDK хочет по умолчанию использовать PKCS12
хранилища ключей, JDK DualFormatPKCS12
откатится к JKS
если чтение файла как PKCS12
завершилось неудачно. Bouncycastle предполагает, что когда javax.net.ssl.trustStoreType
равен pkcs12
, это действительно то, что мы имеем в виду.
Таким образом, чтобы обойти эту проблему, установите системное свойство javax.net.ssl.trustStoreType
равным JKS
:
-Djavax.net.ssl.trustStoreType=JKS
Я подал проблемы с bouncycastle и robolectri c об этом.