I am working on a project that has to support back to API level 16 and requires me to make network calls to dynamic URLs. The issue I am having is that the server recently had to update the TLS to 1.2 for PCI compliance, which by default is disabled on Android API 16-19. While enabling this is not difficult, I am running into an issue with X509 Certificates for those API levels only.
I have done quite a bit of research on it and while all these links were useful:
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
They don't solve the core issue I have which is that I see this exception:
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
When I attempt to connect to a URL that requires TLS1.2 such as https://www.ssllabs.com/ssltest/viewMyClient.html.
Essentially, it comes down to an issue with the TrustManager.
Here are 2 different ways to get the TrustManager object (The first being more secure than the second as the second trusts all certs, which makes SSL a moot point):
First Method:
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
Second Method:
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;
}
};
They are then used with this code:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new TrustManager[] { trustManager }, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
If I use the first TrustManager on API levels 20+, the url listed above will work just fine and the page will load. If I use the first on API levels 16-19, it will throw the exception listed above.
If I use the second TrustManager on any API level 16+, the call will work, but, it leaves a security gap in my application because I am now trusting all certificates and leave myself open to all kinds of attacks.
One other note here is that while the above sample error is from the Volley library, it still happens while using Retrofit with the same results.
My question therefore is, how do I Utilize the X509TrustManager to work with websites like This One on API levels 16-19 like they do on API levels 20+?
Thanks for your time.