OkHttp在Android 4.4及以下不支持TLS协议的解决方法

问题描述

Glide加载https图片:https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates#0

OkHttp在Android 4.4及以下请求https报错:

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)

问题原因

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

Protocol Supported (API Levels) Enabled by default (API Levels)
SSLv3 1–25 1–22
TLSv1 1+ 1+
TLSv1.1 16+ 20+
TLSv1.2 16+ 20+

在 Android 5.0 之前,最高支持的 SSL/TLS 版本为 TLSv1,而这个版本实际上是一个安全性并不是太好的版本,当前已经有许多网站配置为不再支持这种老版本的 SSL/TLS。

在 Android 4.X 的设备上,使用相同版本的 OkHttp 访问最低支持 TLSv1.1 及更低 SSL/TLS 版本的 HTTPS 服务器时,HTTPS 连接会在握手阶段失败。

问题解决

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

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

设置OkHttpClient

public class SSLHelper {

    private static 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.
        }
    }

    private static TrustManager[] getTrustManagers(InputStream... certificates) {
        if (certificates == null || certificates.length <= 0) return null;
        try {
            //构造CertificateFactory对象
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                //得到Certificate并放入到keyStore中
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                }
            }
            //利用keyStore初始化TrustManagerFactory
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            return trustManagers;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client, InputStream... certificates) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            Tls12SocketFactory socketFactory = null;
            TrustManager[] trustManagers = null;
            try {
                trustManagers = getTrustManagers(certificates);
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, trustManagers, null);
                socketFactory = new Tls12SocketFactory(sslContext.getSocketFactory());
                client.sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]);
            } catch (Exception e) {
                Log.e("yezhou", "Error while setting TLS", e);
                if (trustManagers != null) {
                    try {
                        SSLContext sc = SSLContext.getInstance("TLSv1.2");
                        sc.init(null, trustManagers, null);
                        socketFactory = new Tls12SocketFactory(sc.getSocketFactory());
                        client.sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]);
                    } catch (Exception e12) {
                        Log.e("yezhou", "Error while setting TLS 1.2", e12);
                    }
                }
            }

            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);
        }
        return client;
    }
}

初始化OkHttpClient

private OkHttpClient initOkHttpClient()  {
    OkHttpClient builder = OkHttpClient.Builder()
    OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
            .followRedirects(true)
            .followSslRedirects(true)
            .retryOnConnectionFailure(true)
            .cache(null)
            .connectTimeout(10L, TimeUnit.SECONDS)  //设置读取超时时间
            .writeTimeout(10L, TimeUnit.SECONDS)  //设置写的超时时间
            .readTimeout(10L, TimeUnit.SECONDS);  //设置连接超时时间

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        InputStream is = null;
        try {
            is = context.getAssets().open("appblog.cer");
        } catch (IOException e) {
            e.printStackTrace();
        }
        SSLHelper.enableTls12OnPreLollipop(clientBuilder, is);
    }
    return builder.build()
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/18/solution-for-okhttp-not-supporting-tls-protocol-on-android-4-4-and-below/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
OkHttp在Android 4.4及以下不支持TLS协议的解决方法
问题描述 Glide加载https图片:https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates#0 OkHttp在Android 4.4及以……
<<上一篇
下一篇>>
文章目录
关闭
目 录