SSL, Android and Retrofit. Some frustration might occur

SSL is short for Secure Sockets Layer, a protocol developed by Netscape for transmitting private documents and sensitive data via the Internet. SSL uses a cryptographic system that uses two keys to encrypt data −a public key known to everyone and a private or secret key known only to the recipient of the message.

The protocol is frequently used as a way for the application (iOS or Android) to communicate with server (presumably) via RESTful API. The Android end point (client) can be build using various libs that are out there, but arguably the best one is Retrofit. The dictionary definition of retrofitting goes as follows: retrofitting is act of adding a component or accessory to something that did not have it when it was manufactured, which is exactly what this lib does when it comes to client end implementation of and communication with RESTful services. The minimum required Android version is 2.2.

Before you start make sure you have the latest Retrofit lib imported in your project alongside with the corresponding libs (version number is very important) that enable the communication.

  • retrofit-1.7.1
  • okhttp-2.0.0
  • okio-1.0.0
  • okhttp-urlconnection-2.0.0

Simple example of Restrofit implementation is shown below:

 
public class RfitAPI {

// declaration of service interfaces
private static SomeInterface sSomeService;

public interface SomeInterface {
        @GET("/somepath/some_service")
        void someInterfaceMethod(@Query("param_name") String code,
                Callback<SomeResponseObject> callback);

    }

//the adapter 
public static SomeInterface smIntfMthd() {
        if (sSomeService == null) {
            
            RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(
                    "https://somevendor.com").build();
            sSomeService = restAdapter
                    .create(SomeInterface.class);
        }

        return sSomeService;
    }



}
 
public void callSomeMethod(String code) {

                //calling https://somevendor.com/somepath/some_service?param_name=code

        RfitAPI.smIntfMthd().someInterfaceMethod(code, new Callback<SomeResponseObject>(){

            @Override
            public void failure(RetrofitError errRT) {
                //handle error
                
            }

            @Override
            public void success(SomeResponseObject arg0, Response arg1) {
                //handle success
                
            }
            
        });
    }

If you’re running on Android 4+, this code should be sufficient and everything should run smoothly. Yet, there are a couple of things that can go wrong. You might get certification error, or you might not be able to access the service. Most likely lower android versions – 2.3 in my experience, will present some kind of exceptions, but also I’ve seen some devices that run on 4.1.1 to have issues with the certification process.

The first thing you should do is to check the date on your phone (yes, I know you’re smarter than that, but please check). Your device might got restarted, it’s a test device that you only use in the office or whatever may the reason be, there is possibility that the date is not the current one and it’s the reason you’re getting the error.

Second thing when you’re resolving certification issues in this kind of setting is to do a slight modification of the above presented code. It goes as follows:

 
//the adapter 
public static SomeInterface smIntfMthd() {
        if (sSomeService == null) {
                        //adding client instance 
            OkClient client = new OkClient();
            RestAdapter restAdapter = new RestAdapter.Builder().setClient(client).setEndpoint(
                    "https://somevendor.com").build();
            sSomeService = restAdapter
                    .create(SomeInterface.class);
        }

        return sSomeService;
    }

Third tackle of the issue, most likely with SSLHandshakeException, is here or here.

Forth and last and least recommendable way of dealing with SSL problems is based on surpassing the certification process. In order to surpass the handshake process you need to add custom client, and that’s done in the following manner:

 
public 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 null;
                    }
                } };

                // 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 okHttpClient = new OkHttpClient();
                okHttpClient.setSslSocketFactory(sslSocketFactory);
                okHttpClient.setHostnameVerifier(new HostnameVerifier() {
                    
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        // TODO Auto-generated method stub
                        if (hostname.еquals("https://somevendor.com"))
                        return true;
                        else 
                            return false;
                    }
                });

                return okHttpClient;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        
    }
    
    public static OkClient  getOkClient (){
        OkHttpClient client1 = new OkHttpClient();
        client1 = getUnsafeOkHttpClient();
        OkClient _client = new OkClient(client1);
        return _client;
    }

Then change the adapter:

 
public static SomeInterface smIntfMthd() {
        if (sSomeService == null) {
                        //adding unsafe/custom client 
            OkClient client = getOkClient();
            RestAdapter restAdapter = new RestAdapter.Builder().setClient(client).setEndpoint(
                    "https://somevendor.com").build();
            sSomeService = restAdapter
                    .create(SomeInterface.class);
        }

        return sSomeService;
    }

Since this increases the vulnerability of implemented solution, do this only if you know what you’re doing!

On a closing note, if your application includes downloading of images, most likely you’ll have to implement same solution on some level. For image download use Glide or Picasa and add custom clients to handle the exceptions. Check how that’s done, here.

Advertisements

About viksaaskool

What about me?
This entry was posted in Android, AndroidDev, tutorial and tagged , , . Bookmark the permalink.

6 Responses to SSL, Android and Retrofit. Some frustration might occur

  1. Unfortunately, your code produces an error for me: java.net.SocketTimeoutException: timeout

  2. And if I call setConnectionSpecs with ciphers, then I get this error:
    “javax.net.ssl.SSLHandshakeException: Handshake failed”

  3. Ram N. Mansawala says:

    I have got this erroor. Can not resolve Symbol OkClient.

    Which Dependencies is required for this?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s