Android volley error: "Trust anchor for certification path not found", only in real device, not emulator

Viewed 24571

I'm having a problem in my Android app, in one of my fragments I use volley to do a network request:

JsonObjectRequest request = new JsonObjectRequest(
            Request.Method.POST,
            CustomNetworkManager.getInstance(this.getActivity().getApplicationContext()).getRequestUrl(url),
            requestData,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    // process response
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.d("FeedFragment", "Volley error: " + error.toString());
                }
            });

On a real device I get the following error (running API23):

D/FeedFragment: Volley error: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

In an AVD running the same API version it is working fine. I checked other similar threads but couldn't find an answer.

Thanks for your help.

edit: If anyone faces the same error, make sure you don't have any problems with your certificates (http://developer.android.com/intl/pt-br/training/articles/security-ssl.html#CommonProblems)

4 Answers

Just in case one still uses Volley...

Follow the instructions here:

https://developer.android.com/training/articles/security-ssl#java

Download the certificate file (.crt), put it into your assets directory (next to your java and res directories), then change the following code:

InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));

to use the file from assets:

InputStream caInput = new BufferedInputStream(getAssets().open("load-der.crt"));

Forget the part after

// Tell the URLConnection to use a SocketFactory from our SSLContext

and add one single line instead:

HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

Run this code before any connections made.

That's all.

This solved my problem trying to run my android volley app on arc welder only needs to be run once..at the initial splash activity

Step 1: Create a HttpsTrustManager class that implements X509TrustManager:

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HttpsTrustManager implements X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

    @Override
    public void checkClientTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    @Override
    public void checkServerTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {

    }

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers;
    }

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[]{new HttpsTrustManager()};
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    }

}

Step 2: Add HttpsTrustManager.allowAllSSL() before you make an HTTPS request:

HttpsTrustManager.allowAllSSL();
Related