问题出现
前几天在接入新版api的时候,在Android 4.4版本的机型上测试的时候,网络请求捕获了一个异常,SSLProtocolException,具体的异常信息是
com.base.http.exception.NetworkRespException: javax.net.ssl.SSLProtocolException:
SSL handshake aborted: ssl=0x701cc980: Failure in SSL library, usually a protocol error
error:1407743E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert inappropriate fallback
(external/openssl/ssl/s23_clnt.c:744 0x5d873dbc:0x00000000)
查找原因
这个问题到目前还是第一次遇到过,从异常信息可以看到是ssl library中进行http握手时候的错误,发出的握手信息没有得到正确fallback。
由于我们使用的是okhttp框架,所以我就去github repo上查找了issue,果然已经有很多人提了出来,发现这其实是一个Android 系统库的实现问题,对于不同的Android版本,集成的ssl版本也不同。
Protocol | Supported (API Levels) | Enabled by default (API Levels) |
---|---|---|
SSLv3 | 1–TBD | 1–22 |
TLSv1 | 1+ | 1+ |
TLSv1.1 | 20+ | 20+ |
TLSv1.2 | 20+ | 20+ |
可以发现在19及以下的Android版本中,使用的TLS v1的版本,所以对于Android 4.4及以下的机型,要建立https连接,会自动fall back到TLS v1和SSL v3,如果你的https的服务器是1.0以上,就会报出这个异常,我们现在的https的算法是AES256,而TLS v1则是AES128。
解决方案
我们需要让4.4及以下的机型支持 TLS 1.1和TLS 1.2,有人提出了一个方案:
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
client.sslSocketFactory(new SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
client.connectionSpecs(specs);
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
return client;
}
对于okhhtp来说,在创建sslscoketFactory的时候,判断是4.0-5.1的机型,我就手动支持tls 1.2,但我尝试下来,有些机型依然不行,可以供大家作参考。
我的方式是让后端在https服务器上加了一个弱算法的证书来做兼容。
https的升级是大势所趋,会变得更加安全,一些老旧机型没有与之符合的算法,安全和兼容总是很难权衡,我们会逐步提高support level,来满足安全性的需要。