I am using com.loopj.android:android-async-http:1.4.9
for my request to server. It was working fine until I SSL/TLS is required in my server. So I need to modify my AsyncHTTPClient
to use HTTPS in all URLs.
I checked this similar how to make HTTPS calls using AsyncHttpClient? but did not provide clear solution to the problem. The accepted solution was not secure as well because of this warning from the library itself:
Warning! This omits SSL certificate validation on every device, use with caution.
So I went on and check other solutions. I ended up following the recommendation from: https://developer.android.com/training/articles/security-ssl.html. Thus, I have something similar to this:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// 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 an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Get SocketFactory from our SSLContext
//
// !!!PROBLEM IS HERE!!!
//
javax.net.ssl.SSLSocketFactory socketFactory = context.getSocketFactory();
As you can see in the last line, it gives an SSLSocketFactory
from javax.net.ssl
package. However the AsyncHTTPClient
instance requires
asyncHTTPClient.setSSLSocketFactory(cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory)
EDIT:
My server is using a self-signed certificate.
I assume you have a self signed certificate on your server, I dont have a code that does it using com.loopj.android:android-async-http:1.4.9, but I can give you code that uses URLConnection and which loads crt file (my.server.net.crt) from assets folder.:
public static HttpsURLConnection connectSelfSignedHttps(Context ctx, String surl) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
//
InputStream caInput = new BufferedInputStream(ctx.getApplicationContext().getAssets().open("my.server.net.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
//System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// 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 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(surl);
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
}