如何获取信任锚以在Android API级别16-19上正常工作

时间:2018-10-03 15:31:53

标签: java android ssl x509certificate sslhandshakeexception

我正在做一个项目,该项目必须支持回到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上使用第一个,它将抛出上面列出的异常。

如果我在任何16级以上的API级别上使用第二个TrustManager,该调用将起作用,但是,这在我的应用程序中留下了安全漏洞,因为我现在信任所有证书,并且使自己不受各种攻击的影响。

此处需要注意的另一点是,虽然上面的示例错误来自Volley库,但在使用Retrofit并获得相同结果时仍然会发生。

因此,我的问题是,如何利用X509TrustManager在API级别16-19上与This One之类的网站协同工作,就像在API级别20+上一样?

感谢您的时间。

1 个答案:

答案 0 :(得分:0)

您可以针对此错误使用此方法

此错误来自“ 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);
    }
}