https Android 5.0 以下TLS 版本过低造成的问题

 异常如下

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x610df808: Failure in SSL library, usually a protocol error
                                                error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x410f976a:0x00000000)
                                                    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:412)
                                                    at okhttp3.internal.connection.RealConnection.connectTls(Paramount:281)
                                                    at okhttp3.internal.connection.RealConnection.establishProtocol(Paramount:251)
                                                    at okhttp3.internal.connection.RealConnection.connect(Paramount:151)
                                                    at okhttp3.internal.connection.StreamAllocation.findConnection(Paramount:192)
                                                    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(Paramount:121)
                                                    at okhttp3.internal.connection.StreamAllocation.newStream(Paramount:100)
                                                    at okhttp3.internal.connection.ConnectInterceptor.intercept(Paramount:42)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:92)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:67)
                                                    at okhttp3.internal.cache.CacheInterceptor.intercept(Paramount:93)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:92)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:67)
                                                    at okhttp3.internal.http.BridgeInterceptor.intercept(Paramount:93)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:92)
                                                    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(Paramount:120)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:92)
                                                    at okhttp3.internal.http.RealInterceptorChain.proceed(Paramount:67)
                                                    at okhttp3.RealCall.getResponseWithInterceptorChain(Paramount:185)
                                                    at okhttp3.RealCall.execute(Paramount:69)
                                                    at com.qihoo.mm.paramount.tools.glide.j.b(Paramount:44)
                                                    at com.qihoo.mm.paramount.tools.glide.j.a(Paramount:21)
                                                    at com.bumptech.glide.load.b.f$a.b(Paramount:70)
                                                    at com.bumptech.glide.load.b.f$a.a(Paramount:53)
                                                    at com.bumptech.glide.load.engine.a.e(Paramount:170)
                                                    at com.bumptech.glide.load.engine.a.c(Paramount:128)
                                                    at com.bumptech.glide.load.engine.h.f(Paramount:122)
                                                    at com.bumptech.glide.load.engine.h.d(Paramount:101)
                                                    at com.bumptech.glide.load.engine.h.run(Paramount:58)
                                                    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442)
                                                    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
                                                    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
                                                    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
                                                    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
                                                    at java.lang.Thread.run(Thread.java:856)
                                                    at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$a$1.run(Paramount:118)
                                                 Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x610df808: Failure in SSL library, usually a protocol error
                                                error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x410f976a:0x00000000)
                                                    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
                                                    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:371)
                                                        ... 35 more

 

由于集成的 SSL 库版本不同,不同 Android 版本的默认 SSL/TLS 版本配置如下表:

ProtocolSupported (API Levels)Enabled by default (API Levels)
SSLv31–TBD1–22
TLSv11+1+
TLSv1.120+20+
TLSv1.220+20+

在 Android 5.0 之前,最高支持的 SSL/TLS 版本为 TLSv1,而这个版本实际上是一个安全性并不是太好的版本,当前已经有许多网站配置为不再支持这种老版本的 SSL/TLS 。在用 Android 5.0 之前的设备,访问一些不再支持 TLSv1 及之前 SSL/TLS 版本的网站的时候,就会出现一些问题。

 在 Android 4.3、Android 4.2.2 和 Android 4.1.1 上,用相同版本的 OkHttp 访问前面配置为最低支持 TLSv1.1 的 HTTP 服务器时,抛出了完全相同的异常。

客户端版本低于服务器支持的最低 SSL/TLS 版本时,HTTPS 连接会在握手阶段失败。

 

 为请求的网络客户端设置一个特殊的SSLSocketFactory

import android.os.Build
import android.util.Log
import com.android.core.Arrays
import com.qihoo.mm.paramount.env.AppEnv
import okhttp3.OkHttpClient
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.net.UnknownHostException
import java.security.GeneralSecurityException
import java.security.KeyStore
import javax.net.ssl.*
public class Tls12SocketFactory extends SSLSocketFactory {
    private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};

    final SSLSocketFactory delegate;

    public Tls12SocketFactory(SSLSocketFactory base) {
        this.delegate = base;
    }

    @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;
    }
}

 

修改默认X509TrustManager 

 private X509TrustManager getDefaultTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
              TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
                + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

 

设置OkHttpClient

 private OkHttpClient initOkHttpClient()  {
        OkHttpClient builder = OkHttpClient.Builder()
        try {
            if(DEBUG){
                Log.i(TAG,"initOkHttpClient")
            }
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                Tls12SocketFactory.enableTls12OnPreLollipop(builder)
            }
            builder.readTimeout(10L, TimeUnit.SECONDS)//设置读取超时时间
            builder.writeTimeout(10L, TimeUnit.SECONDS)//设置写的超时时间
            builder.connectTimeout(10L, TimeUnit.SECONDS)//设置连接超时时间
        } catch (e: Exception) {
            if(DEBUG){
                Log.e(TAG,"",e)
            }
        }
        return builder.build()
    }

    public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
        if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
            
              try {
                   val sslContext  = SSLContext.getInstance("TLS")
                    sslContext .init(null, null, null)

                    val socketFactory = Tls12SocketFactory(sslContext.socketFactory)
                    client.sslSocketFactory(socketFactory, getDefaultTrustManager())

                } catch (exc: Exception) {
                    if (AppEnv.bAppdebug) {
                        Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
                    }
            try {
                SSLContext sc = SSLContext.getInstance("TLSv1.2");
                sc.init(null, null, null);
                client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
               client.sslSocketFactory(socketFactory, getDefaultTrustManager())


                client.connectionSpecs(specs);
            } catch (Exception exc) {
                if (AppEnv.bAppdebug) {
                    Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
                }
            }
        }

        return client;
    }

 

点赞