Saturday, November 17, 2012

Example for Encrypt and Decrypt using AES with Android 4.2


I'll put in here the test that I did:
As after test the code on my Nexus it's worked encrypting and decrypting (before just encrypt was working), I got a String from an other phone, from SQLite, copied and sent it via email client (GMAIL) to my Nexus, then I have copied it and replace with the one that had been created on 4.2 (the copied one, was from 4.0.4 SII 3G), then started the app again and debugging I could see the correct string returning from this method:
public static String decrypt(String seed, String encrypted) throws Exception {
            byte[] seedByte = seed.getBytes();
            System.arraycopy(seedByte, 0, key, 0, ((seedByte.length < 16) ? seedByte.length : 16));
            String base64 = new String(Base64.decode(encrypted, 0));
            byte[] rawKey = getRawKey(seedByte);
            byte[] enc = toByte(base64);
            byte[] result = decrypt(rawKey, enc);
            return new String(result);
        }


I've made an example to show how it's works.

Activity_main.xml (layout):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/appName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="App name" />

    <TextView
        android:id="@+id/seedName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/appName"
        android:layout_marginTop="5dp"
        android:text="This is the seed." />
    
    <TextView
        android:id="@+id/textToEncrypt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/seedName"
        android:layout_marginTop="5dp"
        android:text="This is the text to encrypt" />

    <TextView
        android:id="@+id/encryptedText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textToEncrypt"
        android:layout_marginTop="5dp"
        android:text="Encrypted text" />

    <TextView
        android:id="@+id/decryptedText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/encryptedText"
        android:layout_marginTop="5dp"
        android:text="Decrypted text" />

    </RelativeLayout>




MainActivity.java :
  1: package com.example.encryptdecrittest;
  2: 
  3:         import android.app.Activity;
  4:         import android.os.Bundle;
  5:         import android.util.Log;
  6:         import android.view.Menu;
  7:         import android.widget.TextView;
  8: 
  9:         public class MainActivity extends Activity {
 10: 
 11:     private String seedWith16Chars = "This is my seed.";
 12:     private String textToEncrypt = "Hi, this is a test to check its gone work or not.";
 13: 
 14:     private TextView seed;
 15:     private TextView text;
 16:     private TextView encryptedValue;
 17:     private TextView decryptedValue;
 18: 
 19: 
 20:     @Override
 21:     protected void onCreate(Bundle savedInstanceState) {
 22:         super.onCreate(savedInstanceState);
 23:         setContentView(R.layout.activity_main);
 24: 
 25:         seed = (TextView) findViewById(R.id.seedName);
 26:         seed.setText(seedWith16Chars);
 27: 
 28:         text = (TextView) findViewById(R.id.textToEncrypt);
 29:         text.setText(textToEncrypt);
 30: 
 31:         encryptedValue = (TextView) findViewById(R.id.encryptedText);
 32:         decryptedValue = (TextView) findViewById(R.id.decryptedText);
 33: 
 34:         try {
 35:             // This value was got when did run it from an 2.3.3 device a Galaxy SII running Android 4.0.4
 36:             String encrypted = "MzA3RDBCMjMxMjQzNzcxREUxMUYxNjg1NzgwOTU1MjU1M0FDOUZEN0M3Q0JGQ0Q5MTI2NEIyNTE2"
 37:                     + "OTQwQTc3NjM2QTBCRDFDMUEyNkUwRjlDMzQwN0U0MEI0NDg2M0JBMDU1OThCNTI1NTZCMEFGNjk1NjJFNzZBMUE0NzM4NTQ=";
 38:             
 39:             // Uncomment the line bellow and comment the line above to run it on an Android 4.1.2 or older.
 40:             // String encrypted = EncodeDecodeAES.encrypt(seedWith16Chars, textToEncrypt);
 41:             Log.e("Encrypt", encrypted);
 42:             encryptedValue.setText(encrypted);
 43: 
 44:             String decrypted = EncodeDecodeAES.decrypt(seedWith16Chars, encrypted);
 45:             decryptedValue.setText(decrypted);
 46:             Log.e("Decrypt", decrypted);
 47:         } catch (Exception e) {
 48:             Log.e("Exception", e.getLocalizedMessage());
 49:         }
 50: 
 51:     }
 52: 
 53: 
 54:     @Override
 55:     public boolean onCreateOptionsMenu(Menu menu) {
 56:         // Inflate the menu; this adds items to the action bar if it is present.
 57:         getMenuInflater().inflate(R.menu.activity_main, menu);
 58:         return true;
 59:     }
 60: 
 61: }
 62: 
 63: 



EncodeDecodeAES.java :
  1: package com.example.encryptdecrittest;
  2: 
  3: import java.security.SecureRandom;
  4: 
  5: import javax.crypto.Cipher;
  6: import javax.crypto.KeyGenerator;
  7: import javax.crypto.SecretKey;
  8: import javax.crypto.spec.SecretKeySpec;
  9: 
 10: import android.util.Base64;
 11: 
 12: public class EncodeDecodeAES {
 13: 
 14:  private final static String HEX = "0123456789ABCDEF";
 15:  private final static int JELLY_BEAN_4_2 = 17;
 16:  private final static byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 17: 
 18: 
 19:  // static {
 20:  // Security.addProvider(new BouncyCastleProvider());
 21:  // }
 22: 
 23:  public static String encrypt(String seed, String cleartext) throws Exception {
 24:   byte[] rawKey = getRawKey(seed.getBytes());
 25:   byte[] result = encrypt(rawKey, cleartext.getBytes());
 26:   String fromHex = toHex(result);
 27:   String base64 = new String(Base64.encodeToString(fromHex.getBytes(), 0));
 28:   return base64;
 29:  }
 30: 
 31: 
 32:  public static String decrypt(String seed, String encrypted) throws Exception {
 33:   byte[] seedByte = seed.getBytes();
 34:   System.arraycopy(seedByte, 0, key, 0, ((seedByte.length < 16) ? seedByte.length : 16));
 35:   String base64 = new String(Base64.decode(encrypted, 0));
 36:   byte[] rawKey = getRawKey(seedByte);
 37:   byte[] enc = toByte(base64);
 38:   byte[] result = decrypt(rawKey, enc);
 39:   return new String(result);
 40:  }
 41: 
 42: 
 43:  public static byte[] encryptBytes(String seed, byte[] cleartext) throws Exception {
 44:   byte[] rawKey = getRawKey(seed.getBytes());
 45:   byte[] result = encrypt(rawKey, cleartext);
 46:   return result;
 47:  }
 48: 
 49: 
 50:  public static byte[] decryptBytes(String seed, byte[] encrypted) throws Exception {
 51:   byte[] rawKey = getRawKey(seed.getBytes());
 52:   byte[] result = decrypt(rawKey, encrypted);
 53:   return result;
 54:  }
 55: 
 56: 
 57:  private static byte[] getRawKey(byte[] seed) throws Exception {
 58:   KeyGenerator kgen = KeyGenerator.getInstance("AES"); // , "SC");
 59:   SecureRandom sr = null;
 60:   if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
 61:    sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
 62:   } else {
 63:    sr = SecureRandom.getInstance("SHA1PRNG");
 64:   }
 65:   sr.setSeed(seed);
 66:   try {
 67:    kgen.init(256, sr);
 68:    // kgen.init(128, sr);
 69:   } catch (Exception e) {
 70:    // Log.w(LOG, "This device doesn't suppor 256bits, trying 192bits.");
 71:    try {
 72:     kgen.init(192, sr);
 73:    } catch (Exception e1) {
 74:     // Log.w(LOG, "This device doesn't suppor 192bits, trying 128bits.");
 75:     kgen.init(128, sr);
 76:    }
 77:   }
 78:   SecretKey skey = kgen.generateKey();
 79:   byte[] raw = skey.getEncoded();
 80:   return raw;
 81:  }
 82: 
 83: 
 84:  private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
 85:   SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
 86:   Cipher cipher = Cipher.getInstance("AES"); // /ECB/PKCS7Padding", "SC");
 87:   cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
 88:   byte[] encrypted = cipher.doFinal(clear);
 89:   return encrypted;
 90:  }
 91: 
 92: 
 93:  private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
 94:   SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
 95:   Cipher cipher = Cipher.getInstance("AES"); // /ECB/PKCS7Padding", "SC");
 96:   cipher.init(Cipher.DECRYPT_MODE, skeySpec);
 97:   byte[] decrypted = cipher.doFinal(encrypted);
 98:   return decrypted;
 99:  }
100: 
101: 
102:  public static String toHex(String txt) {
103:   return toHex(txt.getBytes());
104:  }
105: 
106: 
107:  public static String fromHex(String hex) {
108:   return new String(toByte(hex));
109:  }
110: 
111: 
112:  public static byte[] toByte(String hexString) {
113:   int len = hexString.length() / 2;
114:   byte[] result = new byte[len];
115:   for (int i = 0; i < len; i++)
116:    result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
117:   return result;
118:  }
119: 
120: 
121:  public static String toHex(byte[] buf) {
122:   if (buf == null)
123:    return "";
124:   StringBuffer result = new StringBuffer(2 * buf.length);
125:   for (int i = 0; i < buf.length; i++) {
126:    appendHex(result, buf[i]);
127:   }
128:   return result.toString();
129:  }
130: 
131: 
132:  private static void appendHex(StringBuffer sb, byte b) {
133:   sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
134:  }
135: 
136: }
137: 


Then I did run it from a SII with that line that have a ciphered text commented,

        String encrypted =    "MzA3RDBCMjMxMjQzNzcxREUxMUYxNjg1NzgwOTU1MjU1M0FDOUZEN0M3Q0JGQ0Q5MTI2NEIyNTE2"
                    +   "OTQwQTc3NjM2QTBCRDFDMUEyNkUwRjlDMzQwN0U0MEI0NDg2M0JBMDU1OThCNTI1NTZCMEFGNjk1NjJFNzZBMUE0NzM4NTQ=";

and the one that have

        String encrypted = EncodeDecodeAES.encrypt(seedWith16Chars, textToEncrypt);

Uncomented. Then the LogCat show me the encrypted value, remember that we are still running it from an Anroid 4.0.4 (SII). After that I've copied the text encrypted to be received by **encrypted** String, and unplugged the SII plugged my Nexus with 4.2 and ran the code and worked.
Maybe if you are using a seed (or password) with less than 16 digits it's gone fail. If it's the case try complete it from the text length (that is less then 16th position), with something like spaces or zeros, or even 1 and 0's - 10101..) up to the 16th position.
Also you need to pay attention if your previous code is initializing the keyGen with 128 bits and the class published above try first 256 and then if it's fail go downgrading up to 128bits, then you should comment all of them leaving only the one that has 128.
If you want to download the example project please go to  https://github.com/villacak/EncryptDecritTest.git 
I hope that it helps you.