使用OkHttp进行实际HTTP请求的机器人测试会抛出java.lang.NullPointerException:PKCS#12 KeyStore没有提供密码

问题描述 投票:0回答:1

我正在使用Robolectric 4.3.1testImplementation "org.robolectric:robolectric:4.3.1")创建用于集成测试的Android sqlite环境。我的系统将OkHttpimplementation 'com.squareup.okhttp3:okhttp:3.14.7')用于实际的HTTP请求。我没有使用MockWebServer

升级到Android 10 SDK之后,我不得不将单元测试JVM更新为JDK 9,per the Robolectric instructions

现在严格在Android API 29上运行测试需要Java9或更高版本的运行时。通过Android Studio在API 29上运行测试时,如果看到关于不支持的Java版本的错误;您可以在“运行配置”对话框中使用“ JRE”字段来配置较新的Java运行时。有关更多背景,请参见https://developer.android.com/studio/run/rundebugconfig

但是,现在我的集成测试失败:

java.lang.NullPointerException: No password supplied for PKCS#12 KeyStore.

    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)
    at java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:278)
    at okhttp3.internal.Util.platformTrustManager(Util.java:640)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.java:228)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.java:202)

如何再次进行真正的HTTP调用?

android okhttp robolectric jsse trustmanager
1个回答
0
投票

TLDR

要变通解决此问题,请将javax.net.ssl.trustStoreType系统属性设置为JKS

-Djavax.net.ssl.trustStoreType=JKS

详细信息

我找到了this workaround

尝试了许多事情之后,我终于找到了一种解决方法:

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指定this workaround

解决方法:-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

这是一个很好的线索。我开始使用Android Studio调试JDK 9源代码,并且注意到当我使用org.junit.runners.BlockJUnit4ClassRunner运行时,我的KeyStore.keystoreSpisun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12,但是当我使用sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12运行时,我的org.robolectric.RobolectricTestRunnerKeyStore.keystoreSpi

根据org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi$BCPKCS12KeyStore

此功能将默认密钥库类型从JKS更改为PKCS12。默认情况下,将以PKCS12密钥库格式创建新的密钥库。现有密钥库不会更改,并且密钥库应用程序可以继续显式指定其所需的密钥库类型。

现有应用程序不得被破坏。密钥库通常是长期存在的,因此我们需要支持跨多个JDK版本的访问。访问由JDK早期版本创建的密钥库的应用程序必须在JDK 9上运行不变。类似地,访问由JDK 9创建的密钥库的应用程序应在JDK早期版本上运行不变。

此要求是通过引入既可以了解JKS又可以实现PKCS12格式的密钥库检测机制来实现的。在装入密钥库之前先检查其格式,以确定其类型,然后使用适当的密钥库实现对其进行访问。该机制默认情况下处于启用状态,但可以根据需要禁用。

对此密钥库检测机制的支持可能会反向移植到早期的JDK版本。

因此,经典的org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi$BCPKCS12KeyStore仍然是Java密钥库,我可以验证:

JEP-229

但是,由于JDK希望默认使用JEP-229密钥库,因此如果读取文件失败,则JDK的$JAVA_HOME/lib/security/cacerts将回退到$ 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 。 Bouncycastle假定PKCS12DualFormatPKCS12时,这的确是我们的意思。

因此,要变通解决此问题,请将JKS系统属性设置为PKCS12

javax.net.ssl.trustStoreType

我对此提出了关于pkcs12javax.net.ssl.trustStoreType的问题。

© www.soinside.com 2019 - 2024. All rights reserved.