Wednesday, July 6, 2011

Sending encrypted http requests from Android to a web server that requires basic authentication.

Lately I have been busy ramping up in in php, web services, json, encryption and other web related topics so I can make available an Android app I have been working on to the world.

Here I will write a brief example of how to send encoding http requests from an Android device to a php file in an Apache web server that requires basic authentication, I will omit the code in php but if you need a sample please let me know and I will send it to you.

Everything starts with a call to updateUser() and then you just have to follow the code.

boolean updateUser() throws JSONException{ 

    // In my case the web service I am communicating with expects a JSON message
    // with specific structure, it could have been xml or plain text.

    JSONObject root = new JSONObject();
    JSONObject user = new JSONObject();



    user.put("user_id", "12345");
    user.put("age", "25");
    user.put("name", "Juan G.");
       
    root.put("user_info",user);
    root.put("action","update_user");
        

    //Encrypting the JSON message.
    String jsonEncrypted = encrypt(root.toString());
    String[] result = postCommand(jsonEncrypted);
       
    int statusCode = Integer.parseInt(result[0]);
    if(statusCode>=200 && statusCode<300){
        logd(TAG,"Server returned OK in updateUser()");
        return true;
    }else {           
        logd(TAG,"Server returned ERROR "+ statusCode + " in updateUser()");
    }
   
    return false;        

}

private String[] postCommand(String jsonString){
       
    DefaultHttpClient client = new DefaultHttpClient();

    URI uri = null;
    try {
        uri = URIUtils.createURI(    "http",
                "www.example.com",
                -1,
                "/exampleDir/exampleFile.php",
                null,
                null);
     }catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
     }    

    //This code is needed to authenticate in the web server.
    //If your server does not need authentication you can remove it.
    AuthScope as = new AuthScope(uri.getHost(), uri.getPort(), null);
    Credentials creds = new UsernamePasswordCredentials("ctp_client", "xuGiL1Nu3b=3");       
    client.getCredentialsProvider().setCredentials(as,creds);        
   
    client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000);
       
    HttpPost post = new HttpPost(uri);
    post.addHeader("Accept", "json");                         

    if(jsonString == null){
        return null;
    }
       
    String jsonEncoded = null;
    int statusCode = 0;
       
    try {       
        StringEntity requestEntity = new StringEntity(jsonString,"UTF-8");
        post.setEntity(requestEntity);
           
        HttpResponse response = client.execute(post);
                       
        statusCode = response.getStatusLine().getStatusCode();                                 
        HttpEntity entity = response.getEntity();                   
                               
        if(entity != null){
            InputStream in  = entity.getContent();
            try{
                                  
                DataInputStream dis = new DataInputStream(in);
                           
                StringBuilder sb = new StringBuilder();
                while( (jsonEncoded = dis.readLine()) !=null){
                    sb.append(jsonEncoded +"\n");                                              }
                   
                jsonEncoded = sb.toString();
                logd(TAG,jsonEncoded);
                   
            }finally{
                client.getConnectionManager().shutdown();
                in.close();
            }
        }
    }catch (SocketException e) {
        /*No route to host*/
        statusCode = -1;                   
    }catch(IOException e){
        e.printStackTrace();
    }finally{
        client.getConnectionManager().shutdown();
    }
       
    return new String[]{String.valueOf(statusCode),jsonEncoded};    
}
   
private String encrypt(String plaintext){
       
    int length = plaintext.length();
    int padding = length%16;           
    if(padding != 0){
        plaintext=padRight(plaintext, 16*(length+1));       
    }
       
    byte[] iv = {-27, -49, 25, 63, -91, 15, -88, 60, 96, 27, -78, 48, -35, -71, -24, -99};       
    byte[] key ={17, 25, 38, -74, -41, -110, 17, 21, 16, 108, -98, -89, 12, 72, 80, 3};
       
    SecretKeySpec keyspec = new SecretKeySpec(key, "AES");       
    IvParameterSpec ivspec = new IvParameterSpec(iv);               
           
    byte[] encrypted = null;
    byte[] decrypted = null;
   
    Cipher cipher;       
       
    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
        encrypted = cipher.doFinal( plaintext.getBytes("UTF8"));   
   
    /*
        This is to decrypt the encrypted text but not needed for this example.    
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        decrypted = cipher.doFinal(encrypted);       
    */
 
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (BadPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
               
    return new String(toHexString(encrypted));        
}
   
public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}
   
public static String padRight(String s, int n) {
    return String.format("%1$-" + n + "s", s); 
}


Be careful with the encryption algorithm you choose, for this example the plain text is no more than 200 bytes and after encrypting it the size went to +6K bytes (for this example I omitted other information I am sending in the request). 

Now I am reconsidering whether the value of the information I am transmitting is worth the increasing in size and processing mainly in the server because I have thousands of users that will be communicating with it.

Hope you find it useful!

No comments:

Post a Comment