Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make Android Volley perform HTTPS request, using a certificate self-signed by an Unknown CA?

Before making the question, I found some links, which I checked, one by one, and none of them, gives me a solution:

  • well-kown CA HTTPS request using volley
  • accept all SSL certificates No peer certificate Exception - Volley and Android with self signed certificate
  • Node.js (Socket.io) Socket.io + SSL + self-signed CA certificate gives error when connecting
  • Self-Signed Certificate "MANUALLY" imported: Android SSL HTTP Request using self signed cert and CA

The only link which I have found until now, is this one, which gives two approaches: Making a HTTPS request using Android Volley

  • 1º Instructs to import some classes to your app, when indeed, there are another classes that must be imported, and the classes are using deprecated libs from "apache.org"
  • 2º A example to NUKE all SSL ceriticates (pretty bad idea...)


I also have found this blog, which has a plenty of explanations, but at the end, I realized that the examples are using deprecated libraries from "apache.org" and also, the blog itself doesn't have a content for Android Volley. https://nelenkov.blogspot.mx/2011/12/using-custom-certificate-trust-store-on.html


There is also this link from Android and the code of "Unknown certificate authority" section, which gives a good idea about the solution, but the code itself, lacks something in its structure (Android Studio complaining...): https://developer.android.com/training/articles/security-ssl.html

But this quote from the link, seems the core concept for solving the problem.

"A TrustManager is what the system uses to validate certificates from the server and—by creating one from a KeyStore with one or more CAs—those will be the only CAs trusted by that TrustManager. Given the new TrustManager, the example initializes a new SSLContext which provides an SSLSocketFactory you can use to override the default SSLSocketFactory from HttpsURLConnection. This way the connection will use your CAs for certificate validation."



And now, here is my problem: I have a webserver that is using a self-signed certificate and I have created a "BKS truststore" based on its certificate. I have imported de BKS truststore to my Android APP and now, I have the following code on my App (I'm just posting here the MainActivity, which is the only class that has relevance to this subject until now, I suppose):

package com.domain.myapp;


import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import android.widget.EditText;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import java.io.InputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;


public class LoginScreen extends AppCompatActivity {

Context ctx          = null;
InputStream inStream = null;
HurlStack hurlStack  = null;

EditText username    = null;
EditText password    = null;
String loginStatus   = null;

public LoginScreen() {

    try {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore ks = KeyStore.getInstance("BKS");
        inStream = ctx.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);
        ks.load(inStream, null);
        inStream.close();
        tmf.init(ks);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        hurlStack = new HurlStack(null, sslSocketFactory);
    } catch (Exception e){
        Log.d("Exception:",e.toString());
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login_screen);
    username = (EditText) findViewById(R.id.user);
    password = (EditText) findViewById(R.id.passwd);
}

public void login(View view) {

    RequestQueue queue = Volley.newRequestQueue(this, hurlStack);
    final String url = "https://myserver.domain.com/app/login";

    StringRequest postRequest = new StringRequest(Request.Method.POST, url,
            new Response.Listener<String>()
            {
                @Override
                public void onResponse(String response) {
                    Log.d("Response", response);
                    loginStatus = "OK";
                }
            },
            new Response.ErrorListener()
            {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.d("Error.Response", String.valueOf(error));
                    loginStatus = "NOK";
                }
            }
    ) {
        @Override
        protected Map<String, String> getParams()
        {
            Map<String, String>  params = new HashMap<String, String>();
            params.put("username", String.valueOf(user));
            params.put("domain", String.valueOf(passwd));

            return params;
        }
    };
    queue.add(postRequest);

    if (loginStatus == "OK") {
        Intent intent = new Intent(LoginScreen.this, OptionScreen.class);
        startActivity(intent);
    } else {
        Toast.makeText(getApplicationContext(), "Login failed",Toast.LENGTH_SHORT).show();
    }
}

}

Regarding the constructor class, I took the liberty of copying the code, putting some comments about what do I understand from each part of it:

try {
// I have a TrustManagerFactory object
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// I have a KeyStore considering BKS (BOUNCY CASTLE) KeyStore object
KeyStore ks = KeyStore.getInstance("BKS");
// I have configured a inputStream using my TrustStore file as a Raw Resource
inStream = ctx.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);
// I have loaded my Raw Resource into the KeyStore object
ks.load(inStream, null);
inStream.close();
// I have initialiazed my Trust Manager Factory, using my Key Store Object
tmf.init(ks);
// I have created a new SSL Context object
SSLContext sslContext = SSLContext.getInstance("TLS");
// I have initialized my new SSL Context, with the configured Trust Managers found on my Trust Store
sslContext.init(null, tmf.getTrustManagers(), null);
// I have configured a HttpClientStack, using my brand new Socket Context
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
hurlStack = new HurlStack(null, sslSocketFactory);
} catch (Exception e){
Log.d("Exception:",e.toString());
}

After this, in another Class Method, I have the RequestQueue, using the HttpClientStack which I have configured on the Class COnstructor:

RequestQueue queue = Volley.newRequestQueue(this, hurlStack);
final String url = "https://myserver.domain.com/app/login";
StringRequest postRequest = new StringRequest(Request.Method.POST, url,new Response.Listener<String>()
    {
    ...
    ...
    }

When I run my app, giving the user and password which is expected by my WebServer, I can see in the Android Monitor from Android Studio the following messages:

09-17 21:57:13.842 20617-20617/com.domain.myapp D/Error.Response: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

After all this explanation, I have the following question:

  • What else must be configured in order to make Android accept the SSL certificate from the CA of the custom TrustManager which I have configured at the constructor of the class?

Forgive me, but I'm beginner on Android programming, as well on Java, so maybe, I'm making a terrible mistake...

Any help, would be much appreciated.

UPDATE

I have improved the constructor of the class, doing a better grouping of the statements, and also using the KeyManagerFactory, which seems to be pretty important on this process. Here goes:

public class LoginScreen extends AppCompatActivity {

...
...

  public LoginScreen() {

    try {
        inStream = this.getApplicationContext().getResources().openRawResource(R.raw.mytruststore);

        KeyStore ks = KeyStore.getInstance("BKS");
        ks.load(inStream, "bks*password".toCharArray());
        inStream.close();

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        kmf.init(ks, "bks*password".toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(ks);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(),tmf.getTrustManagers(), null);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        hurlStack = new HurlStack(null, sslSocketFactory);
    } catch (Exception e){
        Log.d("Exception:",e.toString());
    }

  }

...
...

}

Anyway, I'm still having problems..

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

like image 270
codermx Avatar asked Sep 18 '16 04:09

codermx


People also ask

How can I take self-signed certificate in Android?

Go to Settings / Security / Credential storage and select “Install from device storage”. The . crt file will be detected and you will be prompted to enter a certificate name. After importing the certificate, you will find it in Settings / Security / Credential storage / Trusted credentials / User.

How do I set up https certificate?

Under Install and Manage SSL for your site (HTTPS), click Manage SSL Sites. Scroll down to the Install an SSL Website and click Browse Certificates. Select the certificate that you want to activate and click Use Certificate. This will auto-fill the fields for the certificate.

How do I deploy a self-signed certificate?

Import the self-signed certificate to the client Windows computer. On the Windows computer, start MMC (mmc.exe). Add the Certificates snap-in for the computer account and manage certificates for the local computer. Import the self-signed certificate into Trusted Root Certification Authorities > Certificates.

How to make HTTPS request using volley?

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.

How do I use HTTPS on Android?

HTTPS with Client Certificates on Android. Many Android applications use REST or another HTTP based protocol to communicate with a server. Working with HTTP and HTTPS on Android is generally fairly straightforward and well documented. Depending on the version of the Android OS, either HTTPClient or HttpURLConnection “just work”.

Is it possible to add untrusted certificates to volley?

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.

What should I look for in an SSL certificate for Android?

You should also read Android Security Overview as well as Permissions Overview. In a typical SSL usage scenario, a server is configured with a certificate containing a public key as well as a matching private key.


1 Answers

i have implemented https by creating new requestQueue in my volley class by the following code

public RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {
        mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HurlStack(null, newSslSocketFactory()));

    }

    return mRequestQueue;
}

private SSLSocketFactory newSslSocketFactory() {
    try {
        // Get an instance of the Bouncy Castle KeyStore format
        KeyStore trusted = KeyStore.getInstance("BKS");
        // Get the raw resource, which contains the keystore with
        // your trusted certificates (root and any intermediate certs)
        InputStream in = getApplicationContext().getResources().openRawResource(R.raw.keystore);
        try {
            // Initialize the keystore with the provided trusted certificates
            // Provide the password of the keystore
            trusted.load(in, KEYSTORE_PASSWORD);
        } finally {
            in.close();
        }

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

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        SSLSocketFactory sf = context.getSocketFactory();
        return sf;
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}
like image 194
Assem Mahrous Avatar answered Sep 21 '22 06:09

Assem Mahrous