如果服务器不支持SSL,则API 19上的HTTPS连接失败

时间:2018-01-11 10:28:15

标签: android ssl android-emulator

我有一个应用程序,我想在模拟器上用于测试目的。我使用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证书?

更多信息:

  • 服务器使用TLS v1.2
  • 我甚至在模拟器的cacerts文件夹中添加了服务器证书(而不是其CA),结果相同

1 个答案:

答案 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