Android 4.x系统中Glide加载HTTPS自签证书图片失败求助
解决Android 4/5系统中Glide加载自签HTTPS图片的SSL握手问题
我来帮你搞定这个困扰你的问题!你遇到的javax.net.ssl.SSLHandshakeException本质有两个核心原因:一是Android 4.4及以下系统默认不启用TLS 1.2,二是自签证书不在系统信任列表里,导致Glide默认的Http客户端过不了SSL验证。你之前的自定义GlideModule没解决问题,主要是没正确加载自签证书,而且TLS 1.2的兼容实现不够严谨。
完整解决方案
第一步:准备自签证书文件
先把你的自签证书(比如命名为my_cert.crt)放到app/src/main/res/raw目录下——如果没有raw文件夹就自己新建一个。
第二步:替换你的KioskGlideModule代码
把之前的代码完全替换成下面这个版本,我已经修复了证书信任和TLS兼容的问题:
@GlideModule public class KioskGlideModule extends AppGlideModule { @Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { super.registerComponents(context, glide, registry); // 构建支持自签证书+TLS1.2的OkHttpClient OkHttpClient okHttpClient = createCustomOkHttpClient(context); registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient)); } @Override public boolean isManifestParsingEnabled() { return false; } private OkHttpClient createCustomOkHttpClient(Context context) { try { // 1. 加载raw目录里的自签证书 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); InputStream certInputStream = context.getResources().openRawResource(R.raw.my_cert); Certificate certificate; try { certificate = certificateFactory.generateCertificate(certInputStream); } finally { certInputStream.close(); } // 2. 创建包含自签证书的KeyStore String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("my_cert", certificate); // 3. 创建信任这个自签证书的TrustManager String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); // 4. 配置SSLContext,同时兼容TLS1.2 SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, trustManagers, new SecureRandom()); OkHttpClient.Builder builder = new OkHttpClient.Builder() .sslSocketFactory(createTls12SocketFactory(sslContext), (X509TrustManager) trustManagers[0]); // 给Android 4-5系统强制指定TLS版本 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) .build(); builder.connectionSpecs(Collections.singletonList(spec)); } return builder.build(); } catch (Exception e) { Log.e("KioskGlideModule", "创建自定义OkHttpClient失败", e); // 出错时 fallback 到默认客户端,避免应用崩溃 return new OkHttpClient(); } } private SSLSocketFactory createTls12SocketFactory(SSLContext sslContext) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return new SSLSocketFactoryCompat(sslContext.getSocketFactory()); } return sslContext.getSocketFactory(); } // 专门给Android 4.x写的SSLSocketFactory包装类,强制开启TLS1.2 private static class SSLSocketFactoryCompat extends SSLSocketFactory { private final SSLSocketFactory delegate; SSLSocketFactoryCompat(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 enableTls12(delegate.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException { return enableTls12(delegate.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { return enableTls12(delegate.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return enableTls12(delegate.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return enableTls12(delegate.createSocket(address, port, localAddress, localPort)); } // 强制开启TLS1.2,同时保留兼容旧协议 private Socket enableTls12(Socket socket) { if (socket instanceof SSLSocket) { ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}); } return socket; } } }
第三步:检查依赖配置
确保你的app/build.gradle里有这些依赖(版本可以根据你的项目调整,但要保持Glide和OkHttp集成库版本一致):
dependencies { implementation 'com.github.bumptech.glide:glide:4.15.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.github.bumptech.glide:okhttp3-integration:4.15.1' }
第四步:更新Glide加载代码
把原来的Glide调用改成用GlideApp(这是Glide注解处理器自动生成的类,确保你已经Rebuild过项目):
GlideApp.with(getApplicationInstance()) .load(imageUrl) .placeholder(R.drawable.no_image) .error(R.drawable.no_image) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .into(imgView);
关键修复点说明
- 正确信任自签证书:把你的证书加载到KeyStore,让TrustManager明确信任这个证书,解决证书路径验证失败的问题
- TLS1.2兼容增强:通过包装SSLSocketFactory的方式,强制Android 4.x系统启用TLS1.2——这是很多老系统加载HTTPS资源的关键
- 防崩溃处理:添加了完整的异常捕获,就算证书加载出问题,也会fallback到默认客户端,不会导致页面崩溃
- 灵活的协议配置:同时支持TLS1.2/1.1/1.0,确保和不同服务器的兼容性
排查小技巧
- 如果还是崩溃,先检查证书文件是否有效:可以用
openssl x509 -in my_cert.crt -text -noout命令在电脑上验证证书内容 - 确保已经Rebuild项目,Glide注解处理器生成了
GlideApp类——如果没生成,检查依赖里的compiler是否添加正确 - 查看Logcat的错误日志,重点看
KioskGlideModule标签下的异常信息,定位具体问题
内容的提问来源于stack exchange,提问作者Bhoomika Brahmbhatt
相关产品推荐
相关产品推荐

