SSLHandshakeException on Marshmallow

Situation I have built a small android app (sdk 21+) which connects to a server, fetches some data and displays it. For the connection i use the OkHttp library. Running in Android 7+ it all works just fine. Also should mention that i am new to networking and do not have the biggest knowledge yet.

Problem Running on Android 6 (in my case api 23) i get the following exception.

java.security.cert.CertificateException:
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

In my network_security_config.xml i have 3 certificates registered as my trust-anchors I can't make much of this exception and when searching for it on the internet i couldn't find anything helpful either.

Question What could case this problem and how could i fix it? Please try to keep it simple so i can understand it.

So i figured out why the error happens and how to effectively and correctly fix it instead of just overriding my connection and ignoring all certificates how it is suggested everywhere and by everyone.

Turns out the flag android:networkSecurityConfig of the application element in the AndroidManifest.xml is only working on api >= 24. Since my Android 6 phone was running on level 23 it didn't work there and the trust anchors weren't loaded.

java.security.cert.CertificateException:
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

To solve my problem i manually loaded the certificates from the files in the raw resource (i also assigned a name to make it more user-friendly. Thats why i use a Map here, technically a List or an Array would be good enough)

private Map<String, Certificate> createCertificates() throws CertificateException {
    CertificateFactory factory = CertificateFactory.getInstance("X.509");
    InputStream inputProxy = getResources().openRawResource(R.raw.proxy);
    InputStream inputCa = getResources().openRawResource(R.raw.ca);
    Certificate certProxy = factory.generateCertificate(inputProxy);
    Certificate certCa = factory.generateCertificate(inputCa);
    try {
        inputProxy.close();
    } catch (IOException ignore) {
        // will be dumped anyways
    }
    try {
        inputCa.close();
    } catch (IOException ignore) {
        // will be dumped anyways
    }
    Map<String, Certificate> certificates = new HashMap<>();
    certificates.put("CA", certCa);
    certificates.put("PROXY", certProxy);
    return certificates;
}

Then before running any network operations i checked if the api level is < 24. If so, i created my certificates and prompted the user to install them (The data for KeyChain.EXTRA_NAME wouldn't be necessary but is more userfriendly)

if (Build.VERSION.SDK_INT < 24) {
    try {
       Map<String, Certificate> certificates = createCertificates();
       for (String key : certificates.keySet()) {
         Certificate cert = certificates.get(key);
         if (!isCertificateInstalled(cert.getPublicKey())) {
          Intent installIntent = KeyChain.createInstallIntent();
          installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert.getEncoded());
          installIntent.putExtra(KeyChain.EXTRA_NAME, key);
          startActivity(installIntent);
       }
     }
   } catch (CertificateException ignore) {
      // Netzwerkdialog wird später angezeigt
  }
}

But i only prompt the user if the certificate hasn't been installed yet. I check that using the PublicKey of a certificate (in theory not 100% safe, but the chance that someone installs two certificates with the same public key is very very small)

private boolean isCertificateInstalled(PublicKey pPublicKey) {
    try {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((KeyStore) null);
        X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0];
        for (X509Certificate cert : xtm.getAcceptedIssuers()) {
            if (cert.getPublicKey().equals(pPublicKey)) {
                return true;
            }
        }
    } catch (NoSuchAlgorithmException | KeyStoreException ignore) {
        // returns false
    }
    return false;
}

javax.net.ssl.SSLHandshakeException: java.security.cert , SSLHandshakeException:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. Only on Marshmallow and  When attempting to connect to a VNC server via bVNC Pro on my Nexus 9 running Marshmallow I receive the following error: Connection to VNC server failed with reason: [Ljava.lang.StackTraceElement;@1b450f2TLS handshake failed java.net.ssl.SSLHandshakeException: Handshake failed. I had no issues before the upgrade to Marshmallow.

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.

Problem with STARTTLS under Android Marshmallow · Issue #836 , Problem with STARTTLS under Android Marshmallow #836. Closed Caused by: javax.net.ssl.SSLHandshakeException: Handshake failed Marshmallow is LIKELY SAFE for most people when taken by mouth. In some people, it might cause low blood sugar levels. In some people, it might cause low blood sugar levels. Marshmallow is

Just replace your OkHttpClient with below

private static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            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 X509Certificate[0];
                        }
                    }
            };

            // 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();

            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }
                    })
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true)
                    .readTimeout(30, TimeUnit.SECONDS).addInterceptor(new Interceptor() {
                        @Override
                        public okhttp3.Response intercept(Chain chain) throws IOException {
                            Request original = chain.request();

                            Request request = original.newBuilder()
                                    .build();
                            return chain.proceed(request);
                        }
                    }).build();


        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

HTTPS requests on android to a server with a self signed certificate , to get SSLHandshakeException on all of my request to my server. I am testing this app only on android 6.0 version. and yes I use a self  Marshmallow root (Althaea officinalis) is a perennial herb that’s native to Europe, Western Asia, and Northern Africa.It’s been used as a folk remedy for thousands of years to treat digestive

OpenSSLSocketImpl.waitForHandshake() has thrown a , SSLHandshakeException SSLProtocolException with Android 6 (marshmallow). I​'ve an app that communicates with a server through an SSLSocket. Marshmallow is the name for more than just a type of white candy — it’s also a plant native to Africa and parts of Europe that’s used to make an herbal treatment that’s been utilized for centuries. Marshmallow root (Althaea officinalis) has a very long history in folk medicine that goes back to ancient Greek and Egyptian times. Over

SSLHandshakeException, SSLHandshakeException at com.android.org.conscrypt. Android SSLSocket#​getInputstream() throws HandshakeException on earlier versions of Android 6.0. In this case, the SSLHandshakeException occurs because you have a CA that isn't trusted by the system. It could be because you have a certificate from a new CA that isn't yet trusted by Android or your app is running on an older version without the CA.

SSLHandshakeException on Android 4.4 and lower, SSLHandshakeException: Connection closed by peer at com.android.org.​conscrypt.NativeCrypto. I am using Android 6.0, Marshmallow. However, I have tried  His giant cartoon marshmallow mask with the cute eyes was a mask that left an impression on everyone that came across him. Established producers such as Dotcom and Jauz instantly supported his

Comments
  • Please refer this answer stackoverflow.com/questions/25122287/…
  • @sunilkumar already looked that up but i did not quiet understand it. As i said im new to Networking and don't know what functions the TrustStore fullfills etc. How does the TrustStore differ between the version? I thought it is defined by my network_security_config.xml which is loaded the same for all versions
  • Create a trust manager that does not validate certificate chains and try calling your api
  • @ss_ i don't think i can influence which TrustManager is used by my connection since my connection is provided by the OkHttp library. So unless there is some way to insert/override the default TrustManager i don't think this will do the job
  • Found a cleaner way without overriding any Connections or similar stuff. See my answer.