I had the same issue while working with Volley. No HTTPS connections would work with Android Marshmallow and lower. For Nouget and above things were just fine as I was using the following config android:networkSecurityConfig="@xml/network_security_config"
with all my domain specific certificates.
According to the Android documentation:
By default, secure connections (using protocols like TLS and HTTPS) from all apps trust the pre-installed system CAs, and apps targeting Android 6.0 (API level 23) and lower also trust the user-added CA store by default. An app can customize its own connections using base-config (for app-wide customization) or domain-config (for per-domain customization).
So, it makes sense things work differently under Marshmallow. As @Bastu said in his answer:
Turns out the flag android:networkSecurityConfig of the application element in the AndroidManifest.xml is only working on api >= 24
Before finding this question's answer, I stumbled upon this wonderful tutorial. Messing with the code a bit, I ended pulling together this code to be able to use a list of certificates:
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.kitsord.R;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class ExternalConfig {
private static final String TAG = "ExternalConfig";
private static RequestQueue queue;
public static RequestQueue getRequestQueue(final Context applicationContext) {
if (queue == null) {
queue = Volley.newRequestQueue(applicationContext);
if (Build.VERSION.SDK_INT < 24) {
useSSLCertificate(context.getResources(), R.raw.my_certificate1, R.raw.my_certificate2);
}
}
return queue;
}
private static void useSSLCertificate(final Resources resources, final int ... rawCertificateResourceIds) {
final CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (final CertificateException exception) {
Log.e(TAG, "Failed to get an instance of the CertificateFactory.", exception);
return;
}
int i = 0;
final Certificate[] certificates = new Certificate[rawCertificateResourceIds.length];
for (final int rawCertificateResourceId : rawCertificateResourceIds) {
final Certificate certificate;
try (final InputStream certificateInputStream = resources.openRawResource(rawCertificateResourceId)) {
certificate = certificateFactory.generateCertificate(certificateInputStream);
} catch (final IOException | CertificateException exception) {
Log.e(TAG, "Failed to retrieve the Certificate.", exception);
return;
}
certificates[i] = certificate;
i++;
}
final KeyStore keyStore;
try {
keyStore = buildKeyStore(certificates);
} catch (final KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException exception) {
Log.e(TAG, "Failed to build the KeyStore with the Certificate.", exception);
return;
}
final TrustManagerFactory trustManagerFactory;
try {
trustManagerFactory = buildTrustManager(keyStore);
} catch (final KeyStoreException | NoSuchAlgorithmException exception) {
Log.e(TAG, "Failed to build the TrustManagerFactory with the KeyStore.", exception);
return;
}
final SSLContext sslContext;
try {
sslContext = buildSSLContext(trustManagerFactory);
} catch (final KeyManagementException | NoSuchAlgorithmException exception) {
Log.e(TAG, "Failed to build the SSLContext with the TrustManagerFactory.", exception);
return;
}
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
}
private static KeyStore buildKeyStore(final Certificate[] certificates) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
final String keyStoreType = KeyStore.getDefaultType();
final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
int i = 0;
for (final Certificate certificate : certificates) {
keyStore.setCertificateEntry("ca" + i, certificate);
i++;
}
return keyStore;
}
private static TrustManagerFactory buildTrustManager(final KeyStore keyStore) throws KeyStoreException, NoSuchAlgorithmException {
final String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm);
trustManagerFactory.init(keyStore);
return trustManagerFactory;
}
private static SSLContext buildSSLContext(final TrustManagerFactory trustManagerFactory) throws KeyManagementException, NoSuchAlgorithmException {
final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);
return sslContext;
}
}
Now whenever I need a Volley queue, this method will not only let me use the same queue everytime (not sure if it's a bad idea), but will also add my certificate for the https connections. I'm sure this code can be improved.