60
votes

I am trying to make a https request using this code:

RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
request = new Request<String>(Request.Method.GET,"https://devblahblahblah.com/service/etc",errListener);

but I am getting this error:

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

Two points to be noted:

  1. The HTTPS cert is valid. Easily opens without any warning on browser.
  2. The above code works fine with HTTP links.

I actually need to know if there are any switches/options in the Android Volley framework by using which I'll successfully hit a HTTPS URL?

10
My guess is that you will need to configure this the same way you would if directly using HttpUrlConnection. See commonsware.com/blog/2013/03/04/ssl-android-basics.html and nelenkov.blogspot.ie/2011/12/…CommonsWare
I have found a class called HurlStack which extends HttpStack which is an optional param to Volley.newRequestQueue. The constructor of HurlStack accepts a parameter of type SSLSocketFactory and it's written in it's javadoc :"SSL factory to use for HTTPS connections" but haven't tried it yet.Abdullah Shoaib

10 Answers

59
votes

Warning: The following code should not be used in production because it is vulnerable to SSL attacks

Probably these codes below will be helpful for you:

1.Create a HttpsTrustManager class that implements 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());
    }

}

2.Add HttpsTrustManager.allowAllSSL() before you make a https request:

HttpsTrustManager.allowAllSSL();
String  tag_string_req = "string_req";
StringRequest strReq = new StringRequest(Request.Method.POST,
        your_https_url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        Log.d(TAG, "response :"+response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.d(TAG, "Error: " + error.getMessage());
    }
}){
    @Override
    protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<String, String>();
        params.put("username", "max");
        params.put("password", "123456");
        return params;
    }
};
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
16
votes

you can add this class and execut it from onCreate method

new NukeSSLCerts().nuke();

it will make volley to Trust all SSL certificates.

4
votes

So far the only answer talk about adding an untrusted certificate as the solution, but since your browser doesn't complain it usually means Volley can't find the intermediate certificate that does complete the full trusted chain.

It happened to me with LetsEncrypt certificates. Most browsers already have that intermediate certs so on browser everything looks fine, but Volley was apparently missing something.

The solution
Add the intermediate certificate to your webserver config. For Apache you can follow this reference:
https://access.redhat.com/solutions/43575

For LetsEncrypt it specifically is this file: /etc/letsencrypt/live/your.website.com/chain.pem So besides your CertificateFile and KeyFile you should already have working you now have this third line:

SSLCertificateChainFile /etc/letsencrypt/live/your.website.com/chain.pem

Just adding that line, restarting apache and Volley doesn't complain anymore and you didn't introduce any security vulnerabilities!

4
votes

If you are using volley and want to HTTPS request or SSL Certified service then you can choose this easiest way : -->

Step --> 1. keep .cer file into res/raw/ folder.

Step --> 2. Use this method and replace .cer file name with your .cer file and replace your host name also.

private SSLSocketFactory getSocketFactory() {

    CertificateFactory cf = null;
    try {

        cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = getResources().openRawResource(R.raw.cert_name);
        Certificate ca;
        try {

            ca = cf.generateCertificate(caInput);
            Log.e("CERT", "ca=" + ((X509Certificate) ca).getSubjectDN());
        } finally {
            caInput.close();
        }


        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);


        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);


        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {

                Log.e("CipherUsed", session.getCipherSuite());
                return hostname.compareTo("10.199.89.68")==0; //The Hostname of your server.

            }
        };


        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
        SSLContext context = null;
        context = SSLContext.getInstance("TLS");

        context.init(null, tmf.getTrustManagers(), null);
        HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

        SSLSocketFactory sf = context.getSocketFactory();


        return sf;

    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }

    return  null;
}

Step --> 3. Replace this line "RequestQueue queue = Volley.newRequestQueue(this);" with "RequestQueue queue = Volley.newRequestQueue(this, new HurlStack(null, getSocketFactory()));" in request of volley.

1
votes

I couldn't open the link provided by @Ogre_BGR,but while browsing the net I found the actual implementation done in following smanikandan14 Github.Look upon his SSl-connection explanation to understand more about it.

0
votes

This can happen for several reasons, including:

  • The CA that issued the server certificate was unknown
  • The server certificate wasn't signed by a CA, but was self signed
  • The server configuration is missing an intermediate CA

Official doc from android

Solution: you can provide a certificate file within the request

0
votes

For anyone who will come up against a problem like this and you use Letsencrypt for your SSL and node.js for webserver, try this. Assuming you have something like this. I fixed this by adding the line const chain = fs... Hope this helps

...
const app = express();
const privateKey  = fs.readFileSync('ssl/privkey.pem', 'utf8');
const certificate = fs.readFileSync('ssl/cert.pem', 'utf8');
const chain = fs.readFileSync('ssl/chain.pem', 'utf8');
const credentials = {key: privateKey, cert: certificate, ca: chain};
...
var httpsServer = https.createServer(credentials, app);
0
votes

I got the same problem when I add ssl to the domain, After 2 days gone, I found the solution the URL is getting wrong . I was using https://example.com but when I add ssl into domain the url will be change

https://www.example.com

And POST is working fine

0
votes

got this error when i turned off proxy from cloudflare check image here the best solution for this problem is you can turn on proxy back and also add a full secure access on ssl certificate.

0
votes

If anyone is using nginx and SSL certificates from letsencrypt, the solution is to simply use the certificate from file fullchain.pem instead of cert.pem:

ssl_certificate     /.../fullchain.pem;

This file includes the concatenation of your certificate and the CA's.