Making a HTTPS request using Android Volley
Asked Answered
F

10

60

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?

Flowing answered 11/6, 2013 at 13:48 Comment(2)
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/…Calmas
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.Flowing
F
60

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);
Fester answered 22/12, 2014 at 13:1 Comment(7)
This is all good except that you're not actually validating the server certificates. Just be sure to not deploy this in production, unless you want to expose your users to all sorts of SSL attacks.Marquardt
This is not for production.Hobbes
this looks like a simple solution for self sign cert on localhostShantelleshantha
How does this work with Volley like the poster asked?Seltzer
allowAllSSL? this is too much dangerous.Pteranodon
after using the above code, i am facing the following issue in volleyerror "com.android.volley.ParseError: org.json.JSONException: Value <html><head><meta of type java.lang.String cannot be converted to JSONObject". Please suggest me how to fix it.Bristow
Your app will be rejected in Google Play if you use this.Bluebell
B
16

you can add this class and execut it from onCreate method

new NukeSSLCerts().nuke();

it will make volley to Trust all SSL certificates.

Barrault answered 26/4, 2016 at 19:9 Comment(4)
other way is certbot, provide free signed certificate, but only for 90 days, we have to renew it after every 90 daysBarrault
Perfect for DevelopmentTwittery
From 1th of March 2017 Google can block your app: support.google.com/faqs/answer/7188426 "Beginning March 1, 2017, Google Play will block publishing of any new apps or updates that use an unsafe implementation of HostnameVerifier. Your published APK version will remain unaffected, however any updates to the app will be blocked unless you address this vulnerability."Crassus
@Crassus in the second comment i had given, the other approach. To all, only use nukeSSl class, only if you are in development modeBarrault
R
4

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!

Rudyrudyard answered 23/12, 2016 at 14:27 Comment(2)
thanx, this helped, it was not very clear what file exactly i need to edit but I finally figured it out. Though it was different for me since I had to edit in nodejs. But thank you.Musser
Also, to truly track down possible SSL setup errors you can test your site using this tool: ssllabs.com/ssltest/index.html If after using that tool you've suddenly become obsessed with scoring A+, Mozilla has a useful SSL config generator: mozilla.github.io/server-side-tls/ssl-config-generator The modern flavor usually let's you achieve A+ without problems.Rudyrudyard
M
4

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.

Mylohyoid answered 27/2, 2018 at 13:27 Comment(0)
E
1

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.

Eldoneldora answered 2/5, 2014 at 14:1 Comment(0)
A
1

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

Accord answered 8/5, 2020 at 19:51 Comment(0)
B
0

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

Bracci answered 13/2, 2017 at 8:48 Comment(0)
M
0

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);
Musser answered 29/4, 2019 at 15:14 Comment(0)
S
0

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.

Salesmanship answered 31/8, 2020 at 6:21 Comment(0)
E
0

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.

Eliott answered 20/9, 2020 at 18:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.