【发布时间】:2019-03-08 21:35:47
【问题描述】:
我正在开发一个必须支持回 API 级别 16 并要求我对动态 URL 进行网络调用的项目。我遇到的问题是服务器最近必须将 TLS 更新为 1.2 以符合 PCI 标准,默认情况下在 Android API 16-19 上禁用。虽然启用此功能并不困难,但我遇到了仅针对这些 API 级别的 X509 证书的问题。
我对此进行了大量研究,虽然所有这些链接都很有用:
1) How to enable TLS 1.2 support in an Android application (running on Android 4.1 JB)
2) How to use a self signed certificate to connect to a Mqtt server in Android (paho client)?
4) Allowing Java to use an untrusted certificate for SSL/HTTPS connection
5)Trust Anchor not found for Android SSL Connection
6) SSLHandshakeException: Trust anchor for certification path not found. Only on Android API < 19
7) Trusting all certificates using HttpClient over HTTPS
他们没有解决我看到这个异常的核心问题:
com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:151)
at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:112)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:409)
at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
at com.android.okhttp.Connection.connect(Connection.java:107)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136)
at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:110)
at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:96)
... 1 more
当我尝试连接到需要 TLS1.2 的 URL 时,例如 https://www.ssllabs.com/ssltest/viewMyClient.html。
本质上,这归结为 TrustManager 的问题。
这里有两种不同的方式来获取 TrustManager 对象(第一种比第二种更安全,因为第二种信任所有证书,这使得 SSL 成为一个争论点):
第一种方法:
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
第二种方法:
X509TrustManager trustManager = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
然后将它们与以下代码一起使用:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new TrustManager[] { trustManager }, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
如果我在 API 级别 20+ 上使用第一个 TrustManager,上面列出的 url 将正常工作并且页面将加载。如果我在 API 级别 16-19 上使用第一个,它将抛出上面列出的异常。
如果我在任何 API 级别 16+ 上使用第二个 TrustManager,该调用将起作用,但是,它会在我的应用程序中留下一个安全漏洞,因为我现在信任所有证书并且让自己容易受到各种攻击。
这里的另一个注意事项是,虽然上述示例错误来自 Volley 库,但在使用 Retrofit 时仍然会发生相同的结果。
因此,我的问题是,我如何利用 X509TrustManager 与 API 级别 16-19 上的 This One 等网站合作,就像他们在 API 级别 20+ 上一样?
感谢您的宝贵时间。
【问题讨论】:
-
我正在使用套接字的项目中遇到同样的问题。在 API 20+ 上一切正常,但在以下任何情况下,我都会遇到与您描述的相同的信任锚问题,即使我自己提供了正确的证书也是如此。你最终能解决这个问题吗?
-
@Sandervan'tVeer 不,很遗憾。我什至有一周的赏金没有成功的解决方案,所以这可能是那些可能无法解决的问题之一。
标签: java android ssl x509certificate sslhandshakeexception