Sarmad
Sarmad

Reputation: 389

Android 5.0 SSL Connectivity Issue

I am trying to interact with a webservice which is a HTTPS call that works totally fine on different variants of 4.0(I havent checked it below 4.0 so I cant say about them) and its perfectly working. The issue I am facing is on Android 5.0 and the device I was able to grab was Nexus 5 and below is the exception i get when doing connectivity

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate at org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:146) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)

After tonnes of searching and analyzing our production server SSL certificate i figured out that the server accept TLSv1 and the only cipher suite it supports is TLS_RSA_WITH_3DES_EDE_CBC_SHA. Though i understand that its not safe and it should be upgraded but right now i have to find out some way to get my Android app connected with the server.

I tried through the way suggested on this page

https://code.google.com/p/android-developer-preview/issues/attachmentText?id=1200&aid=12000009000&name=CompatSSLSocketFactory.java&token=ABZ6GAcWKpRZhuG6Skof32VtvF0Lzv3Z-A%3A1435550700632

And replaced my required algorithm i.e TLS_RSA_WITH_3DES_EDE_CBC_SHA but now the problem is that i am seeing this exception

Caused by: java.lang.IllegalArgumentException: cipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA is not supported. at com.android.org.conscrypt.NativeCrypto.checkEnabledCipherSuites(NativeCrypto.java:1091) at com.android.org.conscrypt.SSLParametersImpl.setEnabledCipherSuites(SSLParametersImpl.java:244) at com.android.org.conscrypt.OpenSSLSocketImpl.setEnabledCipherSuites(OpenSSLSocketImpl.java:822)

So according to this exception the cipher suite i required is not supported by Android 5.0. But i got puzzled after seeing it in Android 5.0's supported list on this page

http://developer.android.com/reference/javax/net/ssl/SSLEngine.html

Anybody any idea whats this mystery?

Upvotes: 1

Views: 1833

Answers (1)

Sarmad
Sarmad

Reputation: 389

I got the answer finally after working out on the issue for three days. Posting out the correct solution for people who gets stuck in a similar issue in future

First implement CustomTrustManager

public class CustomX509TrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] certs,
                                   String authType) throws CertificateException {

        // Here you can verify the servers certificate. (e.g. against one which is stored on mobile device)

        // InputStream inStream = null;
        // try {
        // inStream = MeaApplication.loadCertAsInputStream();
        // CertificateFactory cf = CertificateFactory.getInstance("X.509");
        // X509Certificate ca = (X509Certificate)
        // cf.generateCertificate(inStream);
        // inStream.close();
        //
        // for (X509Certificate cert : certs) {
        // // Verifing by public key
        // cert.verify(ca.getPublicKey());
        // }
        // } catch (Exception e) {
        // throw new IllegalArgumentException("Untrusted Certificate!");
        // } finally {
        // try {
        // inStream.close();
        // } catch (IOException e) {
        // e.printStackTrace();
        // }
        // }
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

Than implement your own Socket Factory

public class CustomSSLSocketFactory extends SSLSocketFactory {

    SSLContext sslContext = SSLContext.getInstance("TLS");

    public CustomSSLSocketFactory(KeyStore truststore)
            throws NoSuchAlgorithmException, KeyManagementException,
            KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new CustomX509TrustManager();

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    public CustomSSLSocketFactory(SSLContext context)
            throws KeyManagementException, NoSuchAlgorithmException,
            KeyStoreException, UnrecoverableKeyException {
        super(null);
        sslContext = context;
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port,
                               boolean autoClose) throws IOException, UnknownHostException {
        Socket newSocket =  sslContext.getSocketFactory().createSocket(socket, host, port,
                autoClose);
        ((SSLSocket) newSocket).setEnabledCipherSuites(((SSLSocket) newSocket).getSupportedCipherSuites());
        AdjustSocket(newSocket);
        return newSocket;
    }

    @Override
    public Socket createSocket() throws IOException {
        Socket socket = sslContext.getSocketFactory().createSocket();
        ((SSLSocket) socket).setEnabledCipherSuites(((SSLSocket) socket).getSupportedCipherSuites());
        adjustSocket(socket);
        return socket;
    }

    private void adjustSocket(Socket socket)
    {
        String[] cipherSuites = ((SSLSocket) socket).getSSLParameters().getCipherSuites();
        ArrayList<String> cipherSuiteList = new ArrayList<String>(Arrays.asList(cipherSuites));

        cipherSuiteList.add("TLS_RSA_WITH_3DES_EDE_CBC_SHA");
        cipherSuites = cipherSuiteList.toArray(new String[cipherSuiteList.size()]);
        ((SSLSocket) socket).getSSLParameters().setCipherSuites(cipherSuites);

        String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
        ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));

        for (int ii = protocolList.size() - 1; ii >= 0; --ii )
        {
            if ((protocolList.get(ii).contains("SSLv3")) || (protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
                protocolList.remove(ii);
        }

        protocols = protocolList.toArray(new String[protocolList.size()]);
        ((SSLSocket)socket).setEnabledProtocols(protocols);
    }
}

Now add a function in the class to create a HttpClient

public HttpClient createHttpClient(){
            try {
                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(null, null);

                CustomSSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
                sf.setHostnameVerifier(CustomSSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

                HttpParams params = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(params, 15000);
                HttpConnectionParams.setSoTimeout(params, 5000);

                SchemeRegistry registry = new SchemeRegistry();
                registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
                registry.register(new Scheme("https", sf, 443));

                ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

                return new DefaultHttpClient(ccm, params);
            } catch (Exception e) {
                return new DefaultHttpClient();
            }

And now write below lines to call the server/webservice

HttpClient httpClient = createHttpClient();
HttpPost httpost = new HttpPost(url);
HttpResponse response = null;
try {
    response = httpClient.execute(httpost);
    StatusLine statusLine = response.getStatusLine();
} catch (IOException e) {
    e.printStackTrace();
}

Upvotes: 1

Related Questions