Android SSL握手错误:CertPathValidatorException信任锚未找到求助
问题:HTTPS请求突然失效,报
Trust anchor for certification path not found错误 原本正常的HTTPS请求一周后突然失效,已获取站点PEM格式证书(也尝试过证书链),但仍持续出现错误:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
相关代码
建立HTTPS连接的工具方法
public static HttpsURLConnection setUpHttpsConnection(String urlString, Context ctx) { try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream caInput = new BufferedInputStream(ctx.getAssets().open("servercert.pem")); Certificate ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); // Tell the URLConnection to use a SocketFactory from our SSLContext URL url = new URL(urlString); URLEncoder.encode(url.toString(), "UTF-8"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); return urlConnection; } catch (Exception ex) { Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString()); return null; } }
MainActivity中的调用代码
HttpsURLConnection myConnection = setUpHttpsConnection(endpoint.toString(), ctx); //end point is - www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=NVDA&datatype=json&apikey=U89XCI9ILUCICTP1 //fails on the line below if (myConnection.getResponseCode() == 200) { InputStream responseBody = myConnection.getInputStream();
报错堆栈信息
W/System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. W/System.err: at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:362) W/System.err: at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134) W/System.err: at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089) W/System.err: at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876) W/System.err: at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747) W/System.err: at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712) W/System.err: at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:849) W/System.err: at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.access$100(ConscryptEngineSocket.java:722) W/System.err: at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:238)
已配置networkconfig.xml指向证书,并设置了Manifest文件,但仍反复出现相同错误,请求帮助解决。
解决方案
1. 确保证书包含完整信任链
只导入服务器证书通常无法通过验证,需要获取从服务器证书到根CA的完整证书链。可以用以下命令提取目标站点的完整证书链:
openssl s_client -connect www.alphavantage.co:443 -showcerts
将输出中所有-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----的块按顺序(服务器证书→中间CA→根CA)复制到servercert.pem文件中。
2. 修复代码冗余与无效逻辑
代码中重复创建了TrustManagerFactory实例,且URLEncoder.encode(url.toString(), "UTF-8")属于无效逻辑(未使用编码结果,还可能引发异常),直接删除。修改后的工具方法:
public static HttpsURLConnection setUpHttpsConnection(String urlString, Context ctx) { try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream caInput = new BufferedInputStream(ctx.getAssets().open("servercert.pem")); Certificate ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); // 初始化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 context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); // 建立连接 URL url = new URL(urlString); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); // 强制主机名验证,防止中间人攻击 urlConnection.setHostnameVerifier((hostname, session) -> hostname.equals("www.alphavantage.co")); return urlConnection; } catch (Exception ex) { Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString()); return null; } }
3. 核对network_security_config配置
如果使用该配置,需确保证书放在res/raw目录下,且配置文件正确:
<!-- res/xml/network_security_config.xml --> <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">www.alphavantage.co</domain> <trust-anchors> <certificates src="@raw/servercert"/> <!-- 若需保留系统信任CA,添加此行 --> <!-- <certificates src="system"/> --> </trust-anchors> </domain-config> </network-security-config>
并在Manifest中声明:
<application ... android:networkSecurityConfig="@xml/network_security_config">
4. 确认服务器证书状态
请求突然失效大概率是服务器更换了SSL证书,或中间CA证书过期。重新获取最新的证书链导入即可。
5. 调试验证
可以在代码中添加日志,打印加载的证书信息,或使用Android Studio的Network Inspector查看SSL握手细节,确认证书是否被正确识别。
内容的提问来源于stack exchange,提问作者SunnyCoder
相关产品推荐
相关产品推荐

