12

I'm using Volley in Android to perform my app requests. Unfortunately, I'm getting the following error:

com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x61e15f78: Failure in SSL library, usually a protocol error
    error:1407743E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert inappropriate fallback (external/openssl/ssl/s23_clnt.c:744 0x5b647c58:0x00000000)

I'm using two Fragments, inside a ViewPager, which request their content during onResume. The requests url is basically the same but for a query parameter (which set the type of content, e.g. trending vs hot).

The url is in the form https://host/api/content?type={hot/trending}. Authorization is done through the request header.

The weird part about this exception is that only one of the two requests fail and it varies which one from time to time. After I added a delay between them, the exception stopped occurring (oddly pointing to some race condition?). But this seems a bad workaround and I'd like to solve this the right way.

Any thoughts on what could be the cause of it?

EDIT:

The request is created the standard way, using a singleton providing the queue, as follows:

final RequestQueue requestQueue = RequestQueueSingleton.getInstance(getActivity()).getRequestQueue();
final GsonRequestGet<SearchApiWrapper> gsonRequest = new GsonRequestGet<>(clazz, url,successListener, errorListener);
gsonRequest.setRetryPolicy(new DefaultRetryPolicy(3000, 3, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
gsonRequest.setTag(mTag);
requestQueue.add(gsonRequest);

And here is the singleton class:

public class RequestQueueSingleton {

    private static RequestQueueSingleton mInstance;
    private RequestQueue mRequestQueue;
    private Context mContext;

    public RequestQueueSingleton(Context context) {
        mContext = context;
        mRequestQueue = getRequestQueue();
    }

    /**
     * Returns a instance of this singleton
     */
    public static synchronized RequestQueueSingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new RequestQueueSingleton(context);
        }
        return mInstance;
    }

    /**
     * Returns instance of the request queue
     */
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext());
        }
        return mRequestQueue;
    }
}
Edson Menegatti
  • 4,006
  • 2
  • 25
  • 40
  • How are you creating your Volley RequestQueue? – GuilhE Jun 18 '15 at 21:19
  • Take a look at this http://stackoverflow.com/questions/22564317/https-support-for-volley-android-networking-library – EE66 Jun 19 '15 at 08:19
  • @GuilhE I edited the code to add the requestQueue creation logic and how I use it. – Edson Menegatti Jun 19 '15 at 11:52
  • @EE66 I did look into that answer (and others) before asking my question, unfortunately they don't seem to apply to this case because my requests do work regularly. The only failing time is when those two fragments concurrently add their requests to the queue. – Edson Menegatti Jun 19 '15 at 11:56
  • I don't know if this is the cause your if it solves it, but I usually create my queue like this: `static { requestQueue = Volley.newRequestQueue(Application.getContext(), new HurlStack(null, ClientSSLSocketFactory.getSocketFactory())); }` – GuilhE Jun 19 '15 at 14:18
  • @GuilhE and how do you create your ClientSSLSocketFactory? – Edson Menegatti Jun 19 '15 at 16:20
  • @EdsonMenegatti I'm using the same approach that the user Dennis uses in EE66 link (SSLSocketFactory). Will create an answer ;) – GuilhE Jun 19 '15 at 16:46

3 Answers3

3

After our comments maybe this can help you:

Your requestQueue:

static {
    requestQueue = Volley.newRequestQueue(Application.getContext(), new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));
}

The ClientSSLSocketFactory:

public class ClientSSLSocketFactory extends SSLCertificateSocketFactory {
    private SSLContext sslContext;

    public static SSLSocketFactory getSocketFactory(){
        try
        {
            X509TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}

                public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[] { tm }, null);

            SSLSocketFactory ssf = ClientSSLSocketFactory.getDefault(10000, new SSLSessionCache(Application.getInstance()));

            return ssf;
        } catch (Exception ex) {
            return null;
        }
    }

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

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}
GuilhE
  • 11,591
  • 16
  • 75
  • 116
  • Well I would say yes because when we do `sslContext.init(null, new TrustManager[] { tm }, null);` we are using our `tm` that has no implementation. I didn't test it, I'll as soon as I can. – GuilhE Jun 19 '15 at 21:53
  • Unfortunately the issue continues to happen, less frequently but happening still. Any ideas on what could be the cause? – Edson Menegatti Jun 24 '15 at 17:32
  • Well with this approach you are creating a `HurlStack` that allows all SSL certificates so from your side I'm not getting what's wrong. Do you have access to the server? You could try to analyse the server logs to see if they provide any hint. – GuilhE Jun 25 '15 at 09:46
  • @droid_dev I use this class in some of my projects and it works fine in all APIs. What type of error do you have or your server report? – GuilhE Sep 04 '15 at 10:36
  • Basically its a certificate issue. Same error shows `javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.` – moDev Sep 04 '15 at 10:43
  • Check this: https://developer.android.com/training/articles/security-ssl.html#CommonProblems could be one of the problems listed. – GuilhE Sep 04 '15 at 14:10
  • @GuilhE Does accept all certificates introduce any security challenge? – Prashant Jan 05 '18 at 06:10
  • Honestly I don't know. I've moved to Retrofit about 1 year ago and this kind of configurations are way more simple. When you say "security challenge" are your referring to 401 scenarios? If so I guess it depends in you Server logic, what I mean is, your server is responsible to ask for an authentication whenever it's necessary, doesn't really depend on the certificate. – GuilhE Jan 05 '18 at 11:02
  • May I know how to do it in Kotlin? – ロジャー Jul 23 '19 at 10:14
  • We no longer use Volley, switch to Retrofit if possible. Managing certificates with Retrofit is one line of code for example. – GuilhE Jul 23 '19 at 15:58
3

Add following import statments.

import javax.net.ssl.TrustManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import java.util.ResourceBundle;

and add the following code before making network call.

    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    } };
    SSLContext sc = null;
    try {
        sc = SSLContext.getInstance("SSL");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    try {
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
Irshad
  • 3,071
  • 5
  • 30
  • 51
Developine
  • 12,483
  • 8
  • 38
  • 42
0

The only method that worked for me was this

first put below method in your project Application class

private void updateAndroidSecurityProvider() {
    try { 
        ProviderInstaller.installIfNeeded(this); 
    } catch (Exception e) {
        e.getMessage(); 
    } 
}

and then call it in onCreate method.

I'm not faced any issue yet.

reference link

ARN
  • 173
  • 2
  • 9