Android 5.0以下Glide加载https图片问题

问题描述

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

Android 5.0以下Glide加载https图片报错:

class com.bumptech.glide.load.engine.GlideException: Failed to load resource
    There were 2 causes:
    javax.net.ssl.SSLHandshakeException(javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x5dbb9b40: 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 0x59da6890:0x00000000))

问题原因

https单向认证问题。Android 4.4没有启用TLSv1.1 和 TLSv1.2 传输层安全协议:https://developer.android.com/reference/javax/net/ssl/SSLSocket

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 16就已经支持TLS1.1和TLS1.2,但是默认并没有开启,API 20才默认开启

问题解决

为请求的网络客户端设置一个特殊的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;
    }
}

设置AppGlideModule

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        Log.i("yezhou", "MyAppGlideModule.registerComponents: " + Build.VERSION.SDK_INT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                    .followRedirects(true)
                    .followSslRedirects(true)
                    .retryOnConnectionFailure(true)
                    .cache(null)
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10, TimeUnit.SECONDS);
            InputStream is = null;
            try {
                is = context.getAssets().open("appblog.cer");
            } catch (IOException e) {
                e.printStackTrace();
            }
            ModelLoaderFactory factory = new OkHttpUrlLoader.Factory(SSLHelper.enableTls12OnPreLollipop(clientBuilder, is).build());

            registry.replace(GlideUrl.class, InputStream.class, factory);
        }
    }

}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/18/problem-with-loading-https-images-on-glide-under-android-5-0/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Android 5.0以下Glide加载https图片问题
问题描述 Glide加载https图片:https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates#0 Android 5.0以下Glide加……
<<上一篇
下一篇>>
文章目录
关闭
目 录