Retrofit接入可信DigiCert证书仍报不信任链错误求助
嘿,我来帮你搞定这个证书信任的问题!首先先指出你代码里的一个小疏漏,然后再重点解决核心的证书验证失败问题。
首先看你的HttpManager代码:你先build了一个OkHttpClient实例,但之后在DEBUG模式下添加LoggingInterceptor后,又重新build了一次给Retrofit使用。虽然这不影响证书验证,但容易造成混淆,建议调整构建顺序,把DEBUG拦截器的添加放到build之前,只build一次客户端:
OkHttpClient.Builder builder = new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor()) .addInterceptor(chain -> { Request.Builder builder1 = chain.request().newBuilder(); builder1.header("Content-Type", "application/json"); return chain.proceed(builder1.build()); }) .readTimeout(60, TimeUnit.SECONDS) .connectTimeout(60, TimeUnit.SECONDS); // 把DEBUG拦截器的添加移到build之前,避免重复构建 if (BuildConfig.DEBUG){ LoggingInterceptor loggingInterceptor = new LoggingInterceptor.Builder() .loggable(true) .request() .requestTag("Request") .response() .responseTag("Response") .build(); builder.addInterceptor(loggingInterceptor); } // 只构建一次OkHttpClient OkHttpClient client = builder.build();
接下来重点解决你的证书信任错误:
从你贴的错误日志来看,目标服务器的证书是由GeoTrust RSA CA 2018签发的。Chrome、Postman和curl能正常访问,但Android端报错,通常有两个最常见的原因:
原因1:旧Android系统(API级别低于24)不包含该根证书
GeoTrust RSA CA 2018是DigiCert的正规根证书,但Android 7.0(API 24)及以下的系统默认信任库中并没有预装这个证书。而Chrome等浏览器要么自带了更全的信任库,要么会自动从服务器获取缺失的中间证书,所以能正常访问,但Android的OkHttpClient默认不会这么做,导致验证失败。
解决方案:给OkHttpClient添加自定义信任库
你可以手动把这个根证书加入到App的信任库中,步骤如下:
- 获取证书文件:你可以通过Chrome导出目标网站的根证书(地址栏点锁图标 → 证书 → 详细信息 → 复制到文件,选择Base64编码的X.509格式),或者直接获取GeoTrust RSA CA 2018的PEM格式证书。
- 放入项目:把证书重命名为
geotrust_rsa_ca_2018.pem,放到项目的res/raw目录下。 - 编写自定义TrustManager:
private TrustManager[] getCustomTrustManagers(Context context) throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); InputStream certStream = context.getResources().openRawResource(R.raw.geotrust_rsa_ca_2018); Certificate rootCert; try { rootCert = certFactory.generateCertificate(certStream); } finally { certStream.close(); } KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("geotrust_ca", rootCert); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); return trustManagerFactory.getTrustManagers(); }
- 配置OkHttpClient使用这个TrustManager:
// 在构建OkHttpClient.Builder时添加SSL配置 try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, getCustomTrustManagers(context), new SecureRandom()); builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) getCustomTrustManagers(context)[0]); } catch (Exception e) { e.printStackTrace(); }
原因2:服务器的证书链不完整
虽然你验证了证书有效性,但有些服务器只配置了叶证书(就是你日志里的CN=*.url.com那张),没有包含完整的中间证书链。Chrome等工具会自动补全缺失的中间证书,但Android的OkHttpClient默认不会处理这种情况,导致信任链验证失败。
解决方案:检查并修复服务器证书链
你可以用openssl命令检查服务器的证书链:
openssl s_client -connect your-domain.com:443 -showcerts
如果输出的证书列表里只有叶证书,缺少GeoTrust的中间证书,那就需要联系服务器管理员,把完整的证书链(包含叶证书、中间证书、根证书)配置到服务器上。
快速测试验证
如果你想快速确认是不是证书信任的问题,可以临时添加一个信任所有证书的TrustManager(注意:这个只能用于测试,绝对不能放到生产环境):
// 仅用于测试!生产环境禁止使用! builder.sslSocketFactory(new SSLSocketFactoryCompat(), new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) {} @Override public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];} });
如果这样修改后能正常请求,那就百分百确认是证书信任链的问题,回到上面的正规解决方案就行。
内容的提问来源于stack exchange,提问作者Cliff Burton

