Android 8.0+证书路径信任锚缺失异常,适配targetSdkVersion26方案咨询
嘿,这个问题我之前帮不少开发者踩过坑,本质是Android 7.0(API 24)及以上系统对SSL证书验证的机制做了严格调整,再加上你项目里用的是已经被官方弃用的Apache HttpClient,才导致了这种跨版本兼容性问题。下面给你几个符合生产环境要求(targetSdkVersion≥26)的解决方案:
方案一:替换Apache HttpClient为OkHttp(推荐)
Android 6.0(API 23)之后,官方就不再维护Apache HttpClient了,转而推荐使用OkHttp或者HttpURLConnection,它们对高版本系统的SSL验证支持更完善,代码也更简洁。
举个简单的OkHttp GET请求示例:
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("你的接口地址") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { String responseData = response.body().string(); // 处理响应数据 } } });
OkHttp默认会自动验证合法的SSL证书,如果你的服务器证书是权威CA颁发的,直接用这个就能解决问题;如果是自签名证书,也可以通过自定义TrustManager来配置信任,安全性更高。
方案二:保留Apache HttpClient,修复SSL验证逻辑
如果因为历史原因必须保留Apache HttpClient,那需要自定义SSLSocketFactory,手动配置信任的证书链,绝对禁止在生产环境使用“信任所有证书”的不安全代码。步骤如下:
- 导入服务器证书:把服务器的CA证书(.crt或.pem格式)放到项目的
res/raw目录下。 - 编写SSL工具类:加载证书并创建自定义TrustManager:
public class SSLUtils { public static SSLSocketFactory getSSLSocketFactory(Context context) throws Exception { // 加载证书 CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream certInputStream = context.getResources().openRawResource(R.raw.your_cert); Certificate ca; try { ca = cf.generateCertificate(certInputStream); } finally { certInputStream.close(); } // 创建KeyStore String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // 创建TrustManager String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // 创建SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); return new SSLSocketFactory(sslContext); } }
- 替换HttpClient的SSLSocketFactory:
HttpClient httpClient = new DefaultHttpClient(); try { SSLSocketFactory sslSocketFactory = SSLUtils.getSSLSocketFactory(context); Scheme scheme = new Scheme("https", sslSocketFactory, 443); httpClient.getConnectionManager().getSchemeRegistry().register(scheme); } catch (Exception e) { e.printStackTrace(); } // 用这个httpClient发起请求
方案三:检查服务器端证书链完整性
有时候问题出在服务器端:Android 8.0+对SSL证书链的完整性要求更严格,如果服务器只配置了叶子证书,没有配置中间证书,就会导致高版本系统无法找到信任锚点。你可以用SSL证书检测工具检查服务器的证书链是否完整,若不完整,联系服务器运维人员补全中间证书即可。
为什么targetSdkVersion=21能解决?
因为API 21时,Apache HttpClient还是系统默认的网络库,证书验证逻辑相对宽松;而API 24+系统引入了网络安全配置(Network Security Configuration),同时Apache HttpClient被移到了org.apache.http.legacy库,旧的验证逻辑在高targetSdk下不再兼容,所以设置为21能临时规避,但不符合生产环境要求。
内容的提问来源于stack exchange,提问作者Alex

