由于Android系统的不同版本支持的TLS 版本不同,导致Fresco 加载https图片(图片来源于不同服务器,并且配置不一样)出现SSLHandshakeException
TLS 用于在两个通信应用程序之间提供保密性和数据完整性。TLS 1.0可以理解成SSL3.0 的升级版SSL3.1,TLS已有1.0,1.1,1.2,1.3版本。HTTPS与HTTP区别 – TLS/SSL
不同版本的TLS在Android中的支持情况参考Google SSLEngine(需要VPN)
TLSv1.1和TLSv1.2从Android4.1(Api级别16)开始才被支持,从Android4.4 Wear(Api级别20)才被启用(手机是Android5.0,Api级别21)
截了一张AS中的SDK图片
下面是Fresco加载图片出现的2个错误
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ce85028:
Failure in SSL library, usually a protocol error
System.err: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
SSL handshake aborted: ssl=0x6d1da010: Failure in SSL library, usually a protocol error
W/System.err: error:14077102:SSL routines:SSL23_GET_SERVER_HELLO:
unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x63608894:0x00000000)
第一个错误 是图片来源的服务器配置了TLS1.2
第二个错误 是图片来源的服务器配置了TLS1.0
如何查看资源来源TLS版本?打开Google浏览器,按F12,查看Security
既然4.4版本TLS未启用,所以配置SSLSocketFactory启用TLS,
Fresco 可以选择OKHttp库加载图片,然后构建OKHttp的SSLSocketFactory
Gradle 配置:
compile 'com.facebook.fresco:fresco:1.10.0'
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.10.0'
在Application的onCreate()中配置如下:
/**
* Fresco 配置
*/
private void FrescoInit() {
OkHttpClient okHttpClient= getUnsafeOkHttpClient(); // build on your own
ImagePipelineConfig config = OkHttpImagePipelineConfigFactory
.newBuilder(this, okHttpClient)
.build();
Fresco.initialize(this, config);
}
private class TrustAllManager implements X509TrustManager{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
// return new java.security.cert.X509Certificate[]{};
}
}
/**
* 配置OKhttp
* @return
*/
public OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("TLS");
x509TrustManager = new TrustAllManager();
sslContext.init(null, new TrustManager[] { x509TrustManager },
new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
/* final SSLSocketFactory sslSocketFactory = sslContext
.getSocketFactory();*/
final SSLSocketFactory sslSocketFactory = new Tls12SocketFactory(sslContext
.getSocketFactory());
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.sslSocketFactory(sslSocketFactory,x509TrustManager)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Tls12SocketFactory 类extends SSLSocketFactory
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_SUPPORT_VERSION = {"TLSv1","TLSv1.1", "TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
}
return s;
}
}
参考:
Android HTTPS、TLS版本支持相关解决方案
OkHttpUtils: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb8f02e68
不同版本的TLS在Android中的支持情况
https://stackoverflow.com/questions/29916962/javax-net-ssl-sslhandshakeexception-javax-net-ssl-sslprotocolexception-ssl-han
当Fresco/Picasso遇到https(1)----https跳过证书验证
Android 设置客户端支持的TLS支持的版本号