Warning: Use of undefined constant _FILE_ - assumed '_FILE_' (this will throw an Error in a future version of PHP) in /home/bolyarco/www-ikratko/ogrelab/wp-content/plugins/ad-blocker-stats-vip/ad-blocker-stats-vip.php on line 13

Warning: count(): Parameter must be an array or an object that implements Countable in /home/bolyarco/www-ikratko/ogrelab/wp-content/plugins/microkids-related-posts/microkids-related-posts.php on line 645

Using Android Volley With Self-Signed SSL Certificate

Posted / Публикувана 2013-05-28 in category / в категория: Android

Warning: count(): Parameter must be an array or an object that implements Countable in /home/bolyarco/www-ikratko/ogrelab/wp-content/plugins/microkids-related-posts/microkids-related-posts.php on line 645

In brief:

  1. Get Volley from git clone https://android.googlesource.com/platform/frameworks/volley
  2. Get Android Volley Examples project from git clone git://github.com/ogrebgr/android_volley_examples.git
  3. Copy your keystore (BKS format) containing the self-signed public key in res/raw
  4. Open Act_SsSslHttpClient in the examples project, find "R.raw.test" and replace it with your keystore name (without the .pem extension)
  5. Find "new SslHttpClient(" and replace the default password "test123" with the password for your keystore
  6. Replace "44400" with the HTTPS port of your server/virtualhost. If you use the standart 443 -- then you may remove this parameter entirely
  7. Replace "https://tp.bolyartech.com:44400/https_test.html" with your  URL. Please make sure that you are using HTTPS otherwise it will work without as normal request, i.e. without encryption
  8. Start the app, go to "HTTPS with self-signed cert", then "Execute HTTPS request"
  9. If successful you will see something like "This is the result of successful HTTPS request. Congrats!". If some error occurres please check your logcat.
  10. Copy SslHttpClientSslSocketFactorySsX509TrustManager and your keystore to your project and enjoy! :-)

 

In details:

When you create an android app there is  no problem to execute HTTPS request against server with certificate issued by well-known Certification authority. However if you try to you use self-signed certificate you are in trouble -- certificate will be rejected by the trust manager because it cannot be traced to trusted root. Put simply: your certificate does not match any of the built-in (in android) certificates. The solution that I came with is to use external (and newer) HttpClient and provide my own TrustManager that has my self-signed certificate added in.

Few weeks ago, Volley came out. In my previous posting I demonstrated how to use an external HttpClient. Now I will build on that and provide description and example how to use Volley with self-signed certificate.

What will you need:

  • Volley Framework. You can get it using: git clone https://android.googlesource.com/platform/frameworks/volley
  • Volley Android Examples (aka examples). git clone git://github.com/ogrebgr/android_volley_examples.git
  • Your webserver/virtualhost setup with your self-signed certificate
  • the public key of the self-signed certificate (will be explained bellow)

I will assume that you will have Volley and the examples  installed and running. I will assume that you have your server/virtualhost configured to use self-signed certificate (If you need info how to achieve that: this article may help).

1. Preparing your BKS keystore

Android uses keystores in BKS (Bouncy Castle) format. When you generated your certificate it is in PEM format so you will need to import it into a new BKS keystores. Some tutorials that show how to generate your certificate use one and the same file for the private and public keys. You need a file that contains only the public key, i.e. the content starting from "-----BEGIN CERTIFICATE-----" and ending with "-----END CERTIFICATE-----" (inclusive).  If your certificate contains other rows please copy it with new name like my_server_cert.crt and remove those rows. Please note that the result must be plain text file, i.e. don't use fancy editors that may convert it to UTF-8  for example.

In order to create a BKS keystore you will need so called provider jar bcprov-jdk16-146.jar. Please note that you need to use this version. Newer versions will not work with Android (or at least with older versions before 4.1).

Use this command to import your cert into new BKS keystore:

keytool -importcert -v -trustcacerts -file "my_server_cert.crt" -alias imeto_alias -keystore "my.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS

where my_server_cert.crt is your apache certificate and  my.bks is the name of resulting BKS keystore file. Above command assumes that both my_server_cert.crt and bcprov-jdk16-146.jar are in the current directory.

You will be asked to choose some password.

2. Put the BKS keystore in the project

Put my.bks into the example project in directory res/raw.

Follow the steps from "In brief" section above starting from 4.

 

If you need to use self-signed certificates in your own projects you will need following classes SslHttpClientSslSocketFactorySsX509TrustManager. You will need also ExtHttpClientStack if you are not using it yet.

As always any comments, criticism and bug reports will be highly appreciated. :-)

 

 

14 Responses to “Using Android Volley With Self-Signed SSL Certificate”

  1. Peter Hilbring Says:

    Do you have any example about using client ssl authentification with volley?

  2. Ogi Bankov / Огнян Банков Says:

    I am afraid that I don't but it should not be hard to do it:
    There is a description how to do it in principle http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html (Providing an application specific X509KeyManager)

    Basically all you have to do is to load your client certificate into KeyManager and then pass that manager to the sslcontext.init( as first parameter (SslSocketFactory, line 39)

    Please note that self-signed certificate will not work as client certificate if server is not configured to accept it (which is the default).

  3. Peter Hilbring Says:

    Thanks for your response. I'll try that in the next days.

  4. Michael Says:

    Thanks Ognyan.

    I'm somehow stuck with a

    javax.net.SSLPeerUnverifiedExeption: No peer certificate.

    Any idea what's wrong? (I'm using lighttpd)

  5. Hélio Padrão Says:

    It worked, congratulations.

  6. Vacharaphol Says:

    Hi! Ognyan

    I got an error \"com.android.volley.NoConnectionError: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate\" after using Volley.newRequestQueue(getApplicationContext(), new ExtHttpClientStack(new SslHttpClient(keyStore, \"android\", 443))) call to \"https://www.maywebsite.com:443/abcd/login.php\"

    Do you have any suggestion?

  7. Ogi Bankov / Огнян Банков Says:

    @Vacharaphol
    Are you using self-signed certificate?
    Probably it will the best to post the question on http://stackoverflow.com along with more info (like code snippet, type of the certificate, etc). That way more people will see it and will be able to help.

  8. Vacharaphol Says:

    Thanks

  9. Vacharaphol Says:

    I think my problem may come from Apache Virtual Host. I try to get public key using openssl like this "openssl s_client -connect http://www.mywebsite.com:443/abcd/login.php>myweb.pem" and I got another public key.

    Do you have any solution to solve this problem?

  10. Ogi Bankov / Огнян Банков Says:

    @Vacharaphol
    The problem is that by default Apache can have only one SSL certificate per IP per port so in your case the problem is that you are not getting the correct certificate becase there is another virtual host that uses the same IP and port. The solution is to add another HTTPS port in apache's ports.conf for example 44301 and then declare your virtualhost like:
    <VirtualHost 10.42.0.1:44301>
    You will need to replace the IP.
    Restart apache and try to get the certificate again but this time using port 44301 like:
    openssl s_client -connect http://www.mywebsite.com:443/ > myweb.pem

  11. Vacharaphol Says:

    Here is my code…

    {{{

    Runnable r = new Runnable() {

    public void run() {
    try {

    Log.d(TAG, "Get POST data");
    // post parameter
    Map params = new LinkedHashMap();
    params.put("email", email);
    params.put("password", password);

    StringBuilder postData = new StringBuilder();
    for (Map.Entry param : params.entrySet()) {
    if (postData.length() != 0) postData.append('&');
    postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
    postData.append('=');
    postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
    }
    byte[] postDataBytes = postData.toString().getBytes("UTF-8");

    Log.d(TAG, "Load CA from keystore");
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    // use certify which get by by use google chrome
    InputStream caInput = getResources().openRawResource(R.raw.xxx);
    Certificate ca;
    try {
    ca = cf.generateCertificate(caInput);
    Log.d(TAG, "ca=" + ((X509Certificate) ca).getSubjectDN());
    } finally {
    caInput.close();
    }
    Log.d(TAG, "Load CA from keystore finish!!");

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    URL url = new URL("https://www.xxx.com/login.php");
    HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());

    urlConnection.setRequestMethod("POST");
    urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    urlConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
    urlConnection.setDoOutput(true);
    urlConnection.getOutputStream().write(postDataBytes);
    String line;
    StringBuilder sb = new StringBuilder();
    BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
    try {
    while ((line = in.readLine()) != null) {
    sb.append(line);
    }
    } finally {
    if (in != null) {
    in.close();
    }
    }
    Log.d(TAG, sb.toString());
    Log.d(TAG, url.toString());
    Log.d(TAG, "OK! ");
    } catch(Exception e) {
    Log.d(TAG, e.getMessage());
    }
    }

    };
    Thread t = new Thread(r);
    t.start();

    }}}

  12. anthony Says:

    Hi,

    thanks it work. But if i want to mutual auth ? how can i do that? because i try but not work

  13. Ogi Bankov / Огнян Банков Says:

    @anthony
    I am afraid that I don't know -- never tryed it…

  14. anthony Says:

    Thanks! i fixed! and now works with mutual auth!!perfect!