1

I'm just about done working on an app for a local gym, and as my testing is nearly complete, and a version 1 is nearly finished, I'm starting to think about securing the app against any MITM type attacks. While I know the chances are next to zero of someone even wanting to MITM this app (as opposed to say, a banking app), I would still like to be a little proactive in security.

While the app sends/receives no user information (data sent back and forth is stuff like weight, reps, time, the name of the class the user checks in to, etc.), I am transmitting the names of all active gym members (to be used for an auto complete text box). I would like to encrypt the names, but I've been finding it difficult to change my code from HTTP to HTTPS. I've got HTTPS and a self-signed cert on my server, but can't seem to get the android side to work (keep getting no peer cert errors in eclipse). As a work around, I've thought about using AES128 to encrypt/hash each name, then decrypt it on the phone, and then likewise do the same when sending data back through PHP to the database.

Is this a sufficient alternative to encrypting the entire session? Call it "Lazy SSL", as if someone were to get the key, they would be able to decrypt the data, but again, we are only transmitting names, no other user information.

Here is the unencrypted code I'm using (I left out unnecessary stuff to make this block smalller):

public JSONObject makeHttpRequest(String url, String method, List<NameValuePair> params) {

        if (method == "POST") {
            // request method is POST
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new UrlEncodedFormEntity(params));

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();

        } 

This is in a larger class used for parsing Json: My entire JSONParser class

I'm calling this class in places I need to pull or send data to the server, such as the following:

final JSONParser jsonParser = new JSONParser();

    final List<NameValuePair> params = new ArrayList<NameValuePair>();
    params.add(new BasicNameValuePair("tag", Globals.TAG_GETMEMBERS));
    params.add(new BasicNameValuePair("LastRow", lastRow));
    // getting JSON string from URL
    final JSONObject json = jsonParser.makeHttpRequest(
            Globals.WEBSERVICE_URL, "POST", params);

using various resources:

How to enable a self-signed certificate for SSL sockets on Android?

http://randomizedsort.blogspot.com/2010/09/step-to-step-guide-to-programming.html

I was able to get something useful, I originally tried doing the "trust all certs" method, but since that is MITM prone, I would rather not use it (plus it wasn't working. Using the 2nd link I've gotten so far as re-generating the cert, I've downloaded the bouncy castle jar (

I also used the following commands to generate a keystore, and import it into my project:

keytool -genkey -dname "cn = smashwebserver, ou=Development Team, o=Smash Gyms, L=Sunnyvale, s=California, c=US" -alias ssltest -keypass ssltest -keystore c:\dell\ssltest.keystore -storepass ssltest -validity 180

keytool -export -alias ssltest -keystore c:\dell\ssltest.keystore -file c:\dell\ssltest.cer -storepass ssltest -keypass ssltest

keytool -import -alias ssltestcert -file C:\dell\ssltest.cer -keypass ssltestcert -keystore "C:\Users\Evan Richardson\workspace\SmashGyms\res\raw\ssltestcert" -storetype BKS -storepass ssltestcert -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "C:\Users\Evan Richardson\workspace\SmashGyms\libs\bcprov-jdk15on-147.jar"

The resulting JSONParser class block looks like this:

if (method == "POST") {

            // Load the self-signed server certificate
            char[] passphrase = "ssltest".toCharArray();
            KeyStore ksTrust = KeyStore.getInstance("BKS");
            ksTrust.load(context.getResources().openRawResource(
                    R.raw.ssltestcert), passphrase);
            TrustManagerFactory tmf = TrustManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            tmf.init(ksTrust);

            // Create a SSLContext with the certificate
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(),
                    new SecureRandom());

            // request method is POST
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new UrlEncodedFormEntity(params));

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();

        }

however now I get the following error:

10-29 11:55:28.470: W/System.err(9561): java.io.IOException: Wrong version of key store.

I looked that error up, and a possible solution was found here:Android bouncy castle: IOException

I've downloaded the 145 version of bouncycastles Jar, and used that. This fixes the ioexception error, but now I get the following:

10-29 12:21:57.536: W/System.err(12506): Catch exception while startHandshake: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x10b9a10: Failure in SSL library, usually a protocol error
10-29 12:21:57.536: W/System.err(12506): error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:683 0x4026dced:0x00000000)
10-29 12:21:57.536: W/System.err(12506): return an invalid session with invalid cipher suite of SSL_NULL_WITH_NULL_NULL
10-29 12:21:57.586: W/System.err(12506): javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

Strangely enough, if I change my url to "https://google.com", I don't get any errors, just the following:

10-29 14:03:50.198: V/httpresponsetag:(17810): <!DOCTYPE html>
10-29 14:03:50.198: V/httpresponsetag:(17810): <html lang=en>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <meta charset=utf-8>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
10-29 14:03:50.198: V/httpresponsetag:(17810):   <title>Error 405 (Method Not Allowed)!!1</title>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <style>
10-29 14:03:50.198: V/httpresponsetag:(17810):     *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}
10-29 14:03:50.198: V/httpresponsetag:(17810):   </style>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <a href=//www.google.com/><img src=//www.google.com/images/errors/logo_sm.gif alt=Google></a>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <p><b>405.</b> <ins>That’s an error.</ins>
10-29 14:03:50.198: V/httpresponsetag:(17810):   <p>The request method <code>POST</code> is inappropriate for the URL <code>/</code>.  <ins>That’s all we know.</ins>

This may indicate it's in fact my self signed cert, but if i open up https:servername, it works (of course with the default warning)

EDIT:

I was getting the same errors even with accepting all certs, so i went and looked in my browser with the hostname I'm using, same error. I then looked at my NAT settings on my router...I was forwarding to port 80, instead of 443. FAIL. changed to 443, now it looks like it's working, at least with accepting all certs and the following code:

public JSONObject makeHttpRequest(String url, String method,
        List<NameValuePair> params) throws NoSuchAlgorithmException,
        CertificateException, NotFoundException, KeyStoreException,
        KeyManagementException {

    // Making HTTP request
    try {

        // check for request method
        if (method == "POST") {

            // request method is POST
            // defaultHttpClient

            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }

                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }
            } };

            // Install the all-trusting trust manager
            try {
                SSLContext sc = SSLContext.getInstance("SSL");
                sc.init(null, trustAllCerts,
                        new java.security.SecureRandom());
                HttpsURLConnection.setDefaultSSLSocketFactory(sc
                        .getSocketFactory());
            } catch (Exception e) {
            }

            // Now you can access an https URL without having the certificate in the truststore

            HttpClient client = new DefaultHttpClient();
            client = this.sslClient(client);
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new UrlEncodedFormEntity(params));

            // Log.v(TAG, EntityUtils.toString(result.getEntity()));

            HttpResponse httpResponse = client.execute(httpPost);
            // Log.v("httpresponsetag:", EntityUtils.toString(httpResponse
            // .getEntity()));
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();

        }
Community
  • 1
  • 1
Evan R.
  • 1,210
  • 1
  • 27
  • 42
  • 1
    If all user names are, in essence, going to be exposed to an end user via the auto complete itself, why bother ncrypting it? – Mike Brant Oct 29 '12 at 05:19
  • The entire member name list wasn't going to be exposed, well, I suppose it would be if you went through the process of typing out all combinations of names, but that would take a while. The idea was to use an auto complete to pick the person you competed against, to reduce input errors (no caps, misspellings, etc). – Evan R. Oct 29 '12 at 16:38
  • So typically either the data is sensitive and it needs to be encrypted or it is not. Whether or not it is difficult for any user to expose ALL names in your system by typing in autocomplete should not be your criteria for whether this is secure. If you can get ANY name that you are not intending the user to get through this method, then it is not secure. That to me means that with regards to your system, you are not treating this as sensitive data. That is not a problem in and of itself as long as your privacy policy, terms of use, etc. make this clear. – Mike Brant Oct 29 '12 at 20:33
  • I was working on a health-and-fitness app at one point. We considered the possibility that HIPAA regulations might apply, and treated security accordingly. SSH is mature technology -- your time would be better spent figuring out how to make it work than trying to work around it. – Edward Falk Oct 29 '12 at 23:36

3 Answers3

4

Forget about re-inventing lazy-SSL or whatever. Simply use SSL and fix your code. And do not turn off certificate verification and trust all certificates. Using a self-signed certificate is not particularly difficult, post what you have tried and people will point you in the right direction. Generally you need to:

  1. get the certificate
  2. put it in a raw resource in your app
  3. read it and initialize a KeyStore with it
  4. pass this to your SSL socket factory
  5. initialize your HTTP client with the socket factory from 4.

This is how to do it if you are using HttpClient, the point is registering the SSLSocketFactory:

KeyStore ts = KeyStore.getInstance("BKS");
InputStream in = getResources().openRawResource(R.raw.mytruststore);
ts.load(in, TRUSTSTORE_PASSWORD.toCharArray());

SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory
                .getSocketFactory(), 80));
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(ts);
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
HttpParams params = new BasicHttpParams();
ClientConnectionManager cm = 
    new ThreadSafeClientConnManager(params, schemeRegistry);

HttpClient client = new DefaultHttpClient(cm, params);

See this for more examples, a sample project and some background information: http://nelenkov.blogspot.com/2011/12/using-custom-certificate-trust-store-on.html

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • I've been working on trying to get SSL working, but each sample code piece I find never seems to work. I'm wondering if it's the cert, as each time I try to run i get a "no peer certificate" error in eclipse. I'll post my code shortly – Evan R. Oct 29 '12 at 16:58
  • Going to accept this as although no links were provided, or code samples, it is ultimately the best way to prevent MITM attacks by accepting all certs. The answer below works for the time being, but will not be acceptable for the final release, it at least allows me to verify that our server connection will work over SSL. – Evan R. Oct 29 '12 at 23:26
  • Turning off certificate validation does not make you less encrypted, it just does not notify you if you are receiving an untrusted certificate, this is perfectly fine for a development environment, I would not use it in a production one. – Michael Oct 29 '12 at 23:35
  • @Michael Nothing about turning off certificate validation is 'fine'. Even if traffic is super-encrypted, if you are sending it to the wrong place (e.g., the attacker's server), where it gets *decrypted*, what is the point of the whole exercise? – Nikolay Elenkov Oct 30 '12 at 01:09
  • As for the OP's problem: you are properly initializing an `SSLContext`, however, the `HttpClient` is never using it. See updated post for how to initialize `HttpClient`. – Nikolay Elenkov Oct 30 '12 at 01:10
  • @Nikolay - in development environment it does not matter, that is the whole point, you should not create barriers for your development environment. If you send development traffic to the wrong place, nothing is wrong, you just waste a lot of time. – Michael Oct 30 '12 at 04:46
  • Of course it matters. If you put the 'trust all' thing in your code it will just (seem to) work. Both for your dev server and for the production one. And you might even notice that you shipped with it. Then, best case scenario, you end up on the list of broken apps in someone's security paper. Worst case scenario, well... – Nikolay Elenkov Oct 30 '12 at 05:24
1

I don't see why you would want to work around SSL and invent your own encryption scheme. I have a feeling your self-signed cert is causing you issues, perhaps you need to turn off verifying the self-signed cert in eclipse?

Michael
  • 10,124
  • 1
  • 34
  • 49
  • see last edit, I'm partially a retard. I wasn't forwarding to 443, only to 80. With accepting all certs, I had a problem still accessing data, so i started looking at everything again. Now I'll need to figure out how to use just my self-signed cert – Evan R. Oct 29 '12 at 21:41
  • no problem - I have totally done worse and definitely felt stupid. – Michael Oct 29 '12 at 21:44
  • 1
    Michael, I accepted the answer above yours only because it is the best way to prevent any possibility of attack via MITM. Your answer, combined with figuring out the port issue is working well for testing SSL communication between the app and server, and validates that the server is set up correctly. Thank you for your answer though, I appreciate the contribution! – Evan R. Oct 29 '12 at 23:28
  • Thanks, this comment means more than any points could - thanks again, and I am most delighted to know that you got your problems solved. – Michael Oct 29 '12 at 23:34
-2

I understand you are working with ssl but there is one alternative if you would like. It is using encode and decode.

function encodeString($ss,$ntime){

    for($i=0;$i<$ntime;$i++){

        $ss=base64_encode($ss);

    }

    return $ss;

}



function decodeString($ss,$ntime){

    for($i=0;$i<$ntime;$i++){

        $ss=base64_decode($ss);

    }

    return $ss;

}

You can use it like,

 encodeString("$membername", 3); //3 will make the encryption more strong. Higher the value higher the encryption.

 decodeString("$membername", 3); //decodes the member name.
Abhishek Saha
  • 2,564
  • 1
  • 19
  • 29
  • 1
    In what way is this 'sufficiently secure' to 'secur[e] the app against any MITM type attacks'? Not an answer. – user207421 Oct 29 '12 at 06:13