我有一个应用程序,我想在模拟器上用于测试目的。我使用API级别19中的模拟器/平台/系统映像与x86 ABI和Google API。
应用需要将一些数据发送到启用HTTPS的服务器。证书由Let's Encrypt提供,并由X3 CA签名。
尝试发出请求时出现以下错误:
Couldn't execute API call retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall@b0fdfbd8 (Request{method=POST, url=https://hostname.net/api/session, tag=null})
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb80589b0: Failure in SSL library, usually a protocol error
error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0xa90ed990:0x00000000)
at: com.android.org.conscrypt.OpenSSLSocketImpl startHandshake (OpenSSLSocketImpl.java:448)
at: okhttp3.internal.connection.RealConnection connectTls (RealConnection.java:299)
at: okhttp3.internal.connection.RealConnection establishProtocol (RealConnection.java:268)
at: okhttp3.internal.connection.RealConnection connect (RealConnection.java:160)
at: okhttp3.internal.connection.StreamAllocation findConnection (StreamAllocation.java:256)
at: okhttp3.internal.connection.StreamAllocation findHealthyConnection (StreamAllocation.java:134)
at: okhttp3.internal.connection.StreamAllocation newStream (StreamAllocation.java:113)
at: okhttp3.internal.connection.ConnectInterceptor intercept (ConnectInterceptor.java:42)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:147)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:121)
at: okhttp3.internal.cache.CacheInterceptor intercept (CacheInterceptor.java:93)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:147)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:121)
at: okhttp3.internal.http.BridgeInterceptor intercept (BridgeInterceptor.java:93)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:147)
at: okhttp3.internal.http.RetryAndFollowUpInterceptor intercept (RetryAndFollowUpInterceptor.java:125)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:147)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:121)
at: com.redacted.backend.SessionInterceptor intercept (SessionInterceptor.java:61)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:147)
at: okhttp3.internal.http.RealInterceptorChain proceed (RealInterceptorChain.java:121)
at: okhttp3.RealCall getResponseWithInterceptorChain (RealCall.java:200)
at: okhttp3.RealCall execute (RealCall.java:77)
at: retrofit2.OkHttpCall execute (OkHttpCall.java:180)
at: retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall execute (ExecutorCallAdapterFactory.java:91)
at: com.redacted.backend.Backend executeApiCall (Backend.java:139)
at: com.redacted.backend.Backend createSession (Backend.java:62)
at: com.redacted.viewmodel.LoginActivityViewModel$1 run (LoginActivityViewModel.java:50)
at: java.util.concurrent.ThreadPoolExecutor runWorker (ThreadPoolExecutor.java:1112)
at: java.util.concurrent.ThreadPoolExecutor$Worker run (ThreadPoolExecutor.java:587)
at: java.lang.Thread run (Thread.java:841)
所以我认为模拟器有一个过时的CA包,所以我上传了一个在these instructions之后生成的新模型:
这是一个自定义脚本,但它应该很简单。 $device
包含模拟器名称,例如emulator-5554
。
echo "Download lets encrypt X3 CA cert"
[ -f X3.pem ] && rm X3.pem
wget -O X3.pem https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt || fail "Unable to download LE X3 CA cert"
echo "Encode CA cert to emulator format"
filename="$(openssl x509 -in X3.pem -hash -noout).0"
openssl x509 -in X3.pem >$filename
openssl x509 -in X3.pem -text -fingerprint -noout >>$filename
echo "Push encoded CA cert to $device"
$adb -s $device root || fail "Unable to access emulator '$device' as root"
$adb -s $device remount || fail "Unable to remount RW the emulator '$device'"
$adb -s $device push $filename /system/etc/security/cacerts/ || fail "Unable to push encoded CA cert to '$device'"
文件确实被推送了,但我在日志中仍然有相同的错误消息。
有没有办法在模拟器上更新CA证书?
更多信息:
cacerts
文件夹中添加了服务器证书(而不是其CA),结果相同答案 0 :(得分:4)
在早期版本的Android上支持某些版本的TLS有点复杂。 API 1支持TLS v1.0,API 16支持TLS v1.1和v1.2。但是,尽管支持,但TLS v1.1和v1.2未启用 默认情况下为API 20提供客户端套接字。有关详细信息,请参阅SSLSocket documentation。
这意味着默认情况下,从运行API 16,17,18和19的Android设备到仅支持TLS v1.1或v1.2的服务器的SSL连接可能会失败。 Hemant Parmar在评论中发布的链接似乎解释了原因 - 在这些情况下客户端似乎回退到SSLv3,服务器不支持这种情况,因此握手失败。
修复方法是在应用程序代码中专门启用TLS v1.1和/或TLS v1.2支持。有很多文章描述了各种方法 - 例如,How to enable TLS 1.2 support in an Android application (running on Android 4.1 JB),以及您在评论中发布的issue on the okhttp repository。