Retrofit – SSLHandshakeException Android

Retrofit – SSLHandshakeException Android

Recently, I worked on a remote-networking implementation for an Android eCommerce application, and everything went smoothly. I used the Retrofit networking library and the base URL was http://api.infovistar.in without SSL (an HTTP scheme).

The moment the MVP was ready to be beta tested, we acquired a domain name and enabled SSL on the server. Consequently, the base URL scheme became HTTPS (example: https://api.infovistar.in).

The change to a secured URL in the app caused chaos in the entire app. All the endpoints were not connecting to the server successfully. There was a problem with the network handshake between the client and server.

HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Solution 1:

In doing some research, I found out the Android system didn’t trust the server certificate. There are several possible reasons for this:

  • The Certificate Authority (CA) that issued the server certificate was unknown.
  • The server certificate wasn’t signed by a CA but was self-signed.
  • The server configuration is missing an intermediate CA.

In my case, this issue occurred because the server certificate was self-signed.

The Android documentation describes a simple way to set it up to trust self-signed certificates, which I will outline in three steps.

Step 1

Add the .crt or .pem file to the raw folder.

The digital certificate will be obtained from the server or you can request it from the backend engineer.

Step 2

Create an XML network security configuration file (network_security_config.xml) as follows:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">api.infovistar.in</domain>
        <trust-anchors>
            <certificates src="@raw/certificate" />
        </trust-anchors>
    </domain-config>
</network-security-config>

Step 3

Provide the application’s network configuration settings in the Manifest.xml file.

Solution 2:

In your Retrofit request, you need to modify your OKHttpClient.Builder object so that your request will create a certificate that can be trusted by your server. Only then can your server allow access to your request.

    public static Retrofit getUnsafeApiClient(Context ctx) {
        return new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(getUnsafeOkHttpClient().build())
                .build();
    }

    public static OkHttpClient.Builder getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @SuppressLint("TrustAllX509TrustManager")
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                        }

                        @SuppressLint("TrustAllX509TrustManager")
                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                        }

                        @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]);
            return builder;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

These two solutions should allow you to connect seamlessly with the backend.