How to use Cryptography in Android applications ?

When you made an Android applications, you must very often store data in file to persist applications state or games state. For example, you can store some data of a game that lets users to restart game to the same state.

Major concern comes from advanced that would change these data to cheat in your game ! To avoid this potential problem, a good way is to apply Cryptography on data that you store.

To use Cryptography in Android, you can use standard SDK features provided in javax.crypto.* packages. These classes are well known of Java developers.

Main Entry Point is Cipher class that provides functionality of cryptographic cipher for encryption and decryption. It is the core of Java Cryptographic Extension (JCE) framework on Java SE for example.

Its static getInstance() method lets you to get a cipher for a specific cryptographic algorithm.

SecretKey class is also important because it’s an interface representing a secret symmetric key. SecretKeyFactory, that represents a factory for secret keys, is used to convert opaque cryptographic keys into transparent keys representations and vice versa. KeySpec is the interface representing this kind of keys.

Thus, we must use generateSecret() method of SecretKeyFactory instances to transform a KeySpec instance to SecretKey.

Once you get a Cipher instance, you must init it by choosing encrypt or decrypt mode then passing it a SecretKey instance. The cryptographic process is launched when you call doFinal method with bytes’ array to process in parameter.

To put all these concepts in practice, this is a simple cryptographic helper class :


import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class SimpleCrypto {

   private static String SALT = "some_salt_to_change!";
   private final static String HEX = "0123456789ABCDEF";

   public static String encrypt(String seed, String cleartext)
      throws Exception {
      SecretKey key = generateKey(seed.toCharArray(), SALT.getBytes());
      byte[] rawKey = key.getEncoded();
      byte[] result = encrypt(rawKey, cleartext.getBytes());
      return toHex(result);
   }

   public static String decrypt(String seed, String encrypted)
      throws Exception {
      SecretKey key = generateKey(seed.toCharArray(), SALT.getBytes());
      byte[] rawKey = key.getEncoded();
      byte[] enc = toByte(encrypted);
      byte[] result = decrypt(rawKey, enc);
      return new String(result);
   }

   public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
      throws NoSuchAlgorithmException, InvalidKeySpecException {
      // Number of PBKDF2 hardening rounds to use. Larger values increase
      // computation time. You should select a value that causes computation
      // to take >100ms.
      final int iterations = 1000;

      // Generate a 256-bit key
      final int outputKeyLength = 256;

      SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
      KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
      SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
      return secretKey;
   }

   private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
      SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
      Cipher cipher = Cipher.getInstance("AES");
      cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
      byte[] encrypted = cipher.doFinal(clear);
      return encrypted;
   }

   private static byte[] decrypt(byte[] raw, byte[] encrypted)
      throws Exception {
      SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
      Cipher cipher = Cipher.getInstance("AES");
      cipher.init(Cipher.DECRYPT_MODE, skeySpec);
      byte[] decrypted = cipher.doFinal(encrypted);
      return decrypted;
   }

   public static String toHex(String txt) {
      return toHex(txt.getBytes());
   }

   public static String fromHex(String hex) {
      return new String(toByte(hex));
   }

   public static byte[] toByte(String hexString) {
      int len = hexString.length() / 2;
      byte[] result = new byte[len];

      for (int i = 0; i < len; i++)
         result[i] = Integer.valueOf(hexString.
            substring(2 * i, 2 * i + 2),16).byteValue();

      return result;
   }

   public static String toHex(byte[] buf) {
      if (buf == null)
         return "";

      StringBuffer result = new StringBuffer(2 * buf.length);

      for (int i = 0; i < buf.length; i++) {
         appendHex(result, buf[i]);
      }

      return result.toString();
   }

   private static void appendHex(StringBuffer sb, byte b) {
      sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
   }
}

 

And now, how to use our helper class ? To encrypt a string content, you must simply process like this :


public static final String SEED = "Your_seed_....";

// …

String msgToEncode = "Msg to encode";
String encMsg = SimpleCrypto.encrypt(SEED, msgToEncode);

 

Now to decrypt, you must process like this :


String msgToDecode = "Msg to decode";
String decMsg = SimpleCrypto.decrypt(SEED, msgToDecode);

Leave a Reply