2

I tryed to send a POST request to a remote secure server ( https call). First i used the OkHTTP lib to make the call, as this lib worked to make http calls in a previous app, but with https it does not work anymore, see my previous post : Cannot make HTTPS calls with OKHTTP lib

I decided to try the HttpURLConnection to make the call as explained here : https://developer.android.com/reference/java/net/HttpURLConnection.html The code I am using only works on certain devices.

It does work with a Wiko on android 6.0 equiped with SIM card and wifi and does not work on a Samsung on Android 4.2.2 only linked to internet via Wifi.

Here is a logcat of the problem I encounter :

02-23 12:29:59.893 11026-11951 W/System.err: javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.923 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:382)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:231)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:81)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:197)
02-23 12:29:59.923 11026-11951 W/System.err:     at libcore.net.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:281)
02-23 12:29:59.923 11026-11951 W/System.err:     at com.gulplug.gulplugtoolbox.DiagnosisOnline$NetworkAsyncTask.doInBackground(DiagnosisOnline.java:109)
02-23 12:29:59.923 11026-11951 W/System.err:     at com.gulplug.gulplugtoolbox.DiagnosisOnline$NetworkAsyncTask.doInBackground(DiagnosisOnline.java:82)
02-23 12:29:59.923 11026-11951 W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:287)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:234)
02-23 12:29:59.923 11026-11951 W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
02-23 12:29:59.923 11026-11951 W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
02-23 12:29:59.933 11026-11951 W/System.err:     at java.lang.Thread.run(Thread.java:856)
02-23 12:29:59.933 11026-11951 W/System.err: Caused by: java.security.cert.CertificateException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:296)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:197)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:598)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
02-23 12:29:59.933 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:379)
02-23 12:29:59.933 11026-11951 W/System.err:    ... 16 more
02-23 12:29:59.933 11026-11951 W/System.err: Caused by: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
02-23 12:29:59.943 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1475)
02-23 12:29:59.943 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:305)
02-23 12:29:59.943 11026-11951 W/System.err:     at com.sec.android.security.pkix.SecCertPathValidatorSpi.engineValidate(SecCertPathValidatorSpi.java:99)
02-23 12:29:59.953 11026-11951 W/System.err:     at java.security.cert.CertPathValidator.validate(CertPathValidator.java:190)
02-23 12:29:59.953 11026-11951 W/System.err:     at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:283)
02-23 12:29:59.953 11026-11951 W/System.err:    ... 20 more
02-23 12:29:59.953 11026-11951 W/System.err: Caused by: java.security.SignatureException: Signature was not verified
02-23 12:29:59.953 11026-11951 W/System.err:     at org.apache.harmony.security.provider.cert.X509CertImpl.verify(X509CertImpl.java:384)
02-23 12:29:59.953 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.CertPathValidatorUtilities.verifyX509Certificate(CertPathValidatorUtilities.java:1428)
02-23 12:29:59.953 11026-11951 W/System.err:     at com.android.org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(RFC3280CertPathUtilities.java:1470)
02-23 12:29:59.953 11026-11951 W/System.err:    ... 24 more

and the code attached to this log :

class NetworkAsyncTask extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... voids) {
        Authentification authentification = new Authentification();
        authentification.setApplicationToken(applicationtoken);
        Gson gson = new Gson();
        //String json = gson.toJson(authentification);
        String json = "";
        Log.d(TAG, json);
        JSONObject auth = new JSONObject();
        try {
            auth.put("application_token",mytoken);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        String url = myurl;

        try {
            URL object = new URL(url);
            HttpsURLConnection con = (HttpsURLConnection) object.openConnection();

            con.setDoOutput(true);
            con.setDoInput(true);
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("Accept", "application/json");
            con.setRequestProperty("Content-Lenght", String.valueOf(json.getBytes("UTF-8").length));
            con.setRequestMethod("POST");
            con.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());

            OutputStreamWriter wr = null;
            wr = new OutputStreamWriter(con.getOutputStream());
            wr.write(auth.toString());
            wr.flush();

            StringBuilder sb = new StringBuilder();
            int HttpResult = 0;

            HttpResult = con.getResponseCode();

            if (HttpResult == HttpURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(con.getInputStream(), "utf-8"));
                String line = null;
                while ((line = br.readLine()) != null) {
                    sb.append(line + "\n");
                }
                br.close();
                Log.d(TAG, "First log");
                Log.d(TAG, "" + sb.toString());
            } else {
                Log.d(TAG, "response code : " + HttpResult);
                Log.d(TAG, "Http not ok");
                Log.d(TAG, con.getResponseMessage());
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

I don't understand why my first device can validate the signature and the other not. I tryed to sync the date and time as explained here: Could not validate certificate signature?

A possible explanation of my problem here but i don't get it https://groups.google.com/forum/#!topic/android-security-discuss/C_cm3k9SdaM

Does someone encountered the same problem and fixed it ?

UPDT : Both of the methods presented by Alex and Anton are not working with my device. Using Google play service says that provider is up to date. And I still get no signature validation when enabling TLS 1.1 and 1.2 with the method described here : https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/

UPDT 2: Following this link, the server should use TLS 1.1 : https://www.ssllabs.com TLS enabled

Marech
  • 157
  • 12
  • What is the device model? Probably it just doesn't play well with TLS. Could you try to just open the address in Chrome on the device? – Anton Malyshev Feb 24 '18 at 15:12
  • On the galaxy S2 it does not work(android 4.2.2). On the S4 mini it does (android 4.4) as on the wiko LENNY 3 (android 6.0) It is probably a device that dosent play with TLS, i will try to open in chrome as you say. Anyway I am closing this subject as everyone gave a proper answer to my problem. – Marech Feb 26 '18 at 09:49

4 Answers4

3

Android 4.2.2 on older device simply does not support new TLS version used on server. You need to allow to use at least TLS 1.1 on your server, nothing to do on client side.

UPD: TLS 1.2 support is not enabled by default on Android <5, but you can enable its usage using custom SSLSocketFactory including the following method:

private Socket enableTLSOnSocket(Socket socket) {
    if(socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
    }
    return socket;
}

see the link for reference: https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/

Anton Malyshev
  • 8,686
  • 2
  • 27
  • 45
  • Is there any way to get around this TLS version ? I don't have access to the server since I work on a API integration... What about the minimum android version for the TLS version you are talking about, do you have a link ? – Marech Feb 23 '18 at 11:53
  • Here you have an issue reported to OkHttp where you can read about different workarounds: https://github.com/square/okhttp/issues/2372 – Simon Cedergren Malmqvist Feb 23 '18 at 11:54
  • @Marech yes, there is a workaround (I updated the answer), but as Alex said you still can have some problems on older device – Anton Malyshev Feb 23 '18 at 11:59
  • Even with the provided code and link (Have been there, tried that) it will depend on the server and phone combination, but most likely it won't work so it's just wasting time, especially if the server is in AWS cloud. Most likely the server won't accept the certificates the phone has because mostly those certificates are deprecated by now – Alex Feb 23 '18 at 12:00
  • See my update, i will take a look in the reported issue to OkHTTP and see if I can find a workaround – Marech Feb 23 '18 at 12:45
3

As Anton Malyshev said the TLS v1.2 is not supported on Android 4.2.2, matter of fact on devices below Android 5.0, and even if it's supported on some devices you will run into problems with the deprecated certificates.

What you could do is to try to install google ssl provider if the device has play services.

You can read more about on https://developer.android.com/training/articles/security-gms-provider.html

Alex
  • 3,382
  • 2
  • 32
  • 41
2
   private void updateAndroidSecurityProvider() {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (Exception e) {
        e.getMessage();
    }
    }

call above method before calling any HTTP .

and

added below library in gradle:

compile 'com.google.android.gms:play-services-auth:11.8.0'
Gautam Kushwaha
  • 281
  • 3
  • 15
1

I was able to achieve this using OkHttp with Retrofit2. It's written in Kotlin.

You can add this on start of your app possible in your launcher activity:

    try {
           SSLContext.getInstance("TLSv1.2")
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        }

        try {
            ProviderInstaller.installIfNeeded(context)
        } catch (e: Exception) {
            e.printStackTrace()
        }

Also add this on your OkHttp Builder:

fun provideSecureClient(): OkHttpClient {

    try {
        val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
        trustManagerFactory.init(null as KeyStore?)
        val trustManagers = trustManagerFactory.trustManagers
        if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
            throw IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers))
        }

        val trustManager = trustManagers[0] as X509TrustManager
        val sslContext = SSLContext.getInstance("SSL")
        sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
        val sslSocketFactory = sslContext.socketFactory

        return OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory, trustManager)
                .build()

    } catch (e: Exception) {
        throw RuntimeException(e)
    }

}
Umair Adil
  • 968
  • 1
  • 10
  • 24