如何让信任锚在 Android API 级别 16-19 上正常工作
Posted
技术标签:
【中文标题】如何让信任锚在 Android API 级别 16-19 上正常工作【英文标题】:How to get Trust anchors to work properly on Android API levels 16-19 【发布时间】: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)?
3)SSLSocketFactory in java
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 不,很遗憾。我什至有一周的赏金没有成功的解决方案,所以这可能是那些可能无法解决的问题之一。 【参考方案1】:这个错误可以用这个方法
此错误来自“https://”,我们在基本网址中使用了“s”
并将 connectTimeout 时间更改为您的相应时间
public static OkHttpClient getUnsafeOkHttpClient()
try
final TrustManager[] trustAllCerts = new TrustManager[]
new X509TrustManager()
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
return new java.security.cert.X509Certificate[];
;
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier()
@Override
public boolean verify(String hostname, SSLSession session)
return true;
);
builder.connectTimeout(6000, TimeUnit.SECONDS)
.readTimeout(6000, TimeUnit.SECONDS)
.writeTimeout(6000, TimeUnit.SECONDS).connectionPool(new ConnectionPool(50, 50000, TimeUnit.SECONDS));
return builder.build();
catch (Exception e)
throw new RuntimeException(e);
【讨论】:
据我所知,更改我的 TrustManager 以匹配您上面的代码只会允许所有事情通过,正如我在问题中提到的,这是我试图避免的一个主要安全漏洞。以上是关于如何让信任锚在 Android API 级别 16-19 上正常工作的主要内容,如果未能解决你的问题,请参考以下文章
Android studio:调用需要 API 级别 16 错误