公司进行中台化改造,然后我们在调用Serverless服务的时候发现Android4.x设备出现了SSLHandshakeException
经过查阅资料初步怀疑我们的Serverless最低只兼容TLS1.1,而Android4.X设备没有开启TLS1.1,TLS1.2版本的协议所导致。
Android4.2设备默认支持以下的 SSL/TLS 协议版本:
如果是协议版本不匹配,那么在请求的时候打开Android的TLS1.1和TLS1.2不就行了吗?于是Android使用OkHttp为例,在构建HttpClient的时候打开TLS1.1和TLS1.2:
private X509TrustManager xtm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
};
new OkHttpClient.Builder()
.sslSocketFactory(new TLSSocketFactory(xtm), xtm).build();
TLSSocketFactory类里面代码:
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public TLSSocketFactory(X509TrustManager xtm) throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[]{xtm}, new SecureRandom());
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(delegate.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if (socket instanceof SSLSocket) {
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
最近在进行老服务到新服务的迁移,在迁移之后,我们使用Adnroid4.X设备的用户再次出现的网络异常的现象,具体的报错信息:
Connection error: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSLhandshake aborted: ssl=0xb8e394a0: Failure in SSL library,usually a protocol error error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
刚开始以为和之前碰到的问题一样,所以也就一直往这方面排查,然后经过测试发现并不是一样的情况。于是,开始在指定的设备进行网络抓包分析:
在使用Wireshark进行抓包分析,发现在SSL握手的过程中没有server hello,直接就 Handshake Failure:
而正常的TLS1.2握手过程是这样的:
然后我们更换Android5.0进行测试,发现一切正常,经过对Android4.2的请求和Android5.0的请求进行对比发现他们所支持的Cipher Suite加密套件不一样,Android5.0设备在进行TLS握手的时候服务端选择的是:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
下图是客户端在进行Client Hello的时候向服务端发送自身所支持的加密套件:
下图是服务端在Server Hello的时候与客户端匹配的加密套件:
而在Android4.2的设备上发现所支持的加密套件少了好多:
新服务添加相兼容的加密套件,添加TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA以后,恢复正常。