Android Encryption

how to securely store encryption keys in android?

From your comments, you need to encrypt data using a local key for current Android versions and the old ones

Android Keystore is designed to generate and protect your keys. But it is not available for API level below 18 and it has some limitations until API level 23.

You will need a random symmetric encryption key, for example AES. The AES key is used to encrypt and decrypt you data. I'm going to summarize your options to generate and store it safely depending on Android API level.

  • API Level < 18: Android Keystore not present. Request a password to the user, derive an encryption key from the password, The drawback is that you need to prompt for the password when application starts. The encryption key it is not stored in the device. It is calculated each time when the application is started using the password

  • API Level >=18 <23: Android Keystore available without AES support. Generate a random AES key using the default cryptographic provider (not using AndroidKeystore). Generate a RSA key pair into Android Keystore, and encrypt the AES key using RSA public key. Store encrypted AES key into Android SharedPreferences. When application starts, decrypt the AES key using RSA private key

  • API Level >=23: Android Keystore available with AES support. Generate a random AES key using into Android Keystore. You can use it directly.

To encrypt to can use AES/CBC/PKCS7Padding algorithm. It requires also a random initialization vector (IV) to encrypt your data, but it can be public.

Alternatives:

  • API level >14: Android Key Chain: KeyChain is a system-wide credential storage. You can install certificates with private keys that can be used by applications. Use a preinstalled key to encrypt/decrypt your AES key as shown in the second case above.

  • External token: The protected keys are not stored in the device. You can use an external token containing a private/public key pair that allows you to encrypt the AES key. The token can be accesed using bluetooth or NFC

How to protect my encryption key in Android?

You can use Andriod Keystore to encrypt your SQLCipher password.

I had the same issue while ago, where SQLCipher was used to secure data, but password itself was not. This allowed a security flaw where a simple decompilation would reveal the password as it was in the form of string constant.

My solution was:

  • Generate a random number when app starts at first. (You can change this behaviour for whatever suits you)
  • Encrypt this number using Android Keystore.
  • The original form of the number is gone once its encrypted.
  • Save this in Prefs.
  • Now, whenever SQLCipher needs password, it will decrypt it and use it.
  • Since Android Keystore is providing keys at runtime, and keys are strictly app specific, it will be hard to break this database.
  • Although everything can be broken but this approach will make it a lot harder for the attacker to retrieve data from DB, or DB password.

Here is a sample project I made which also has a SQLCipher use case same as yours.

Encryption Helper for Encrypting Passwords

Use case for SQLCipher

Note that the term you are using as encryption key is used as password/number for DB in above discussion.

Easy way to Encrypt/Decrypt string in Android

You can use Cipher for this.

This class provides the functionality of a cryptographic cipher for encryption and decryption. It forms the core of the Java Cryptographic Extension (JCE) framework.

Sample of encryption and decryption:

public static SecretKey generateKey() 
throws NoSuchAlgorithmException, InvalidKeySpecException
{
return secret = new SecretKeySpec(password.getBytes(), "AES");
}

public static byte[] encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException
{
/* Encrypt the message. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return cipherText;
}

public static String decryptMsg(byte[] cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException
{
/* Decrypt the message, given derived encContentValues and initialization vector. */
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(cipherText), "UTF-8");
return decryptString;
}

To encrypt:

SecretKey secret = generateKey();
EncUtil.encryptMsg(String toEncrypt, secret))

To decrypt:

EncUtil.decryptMsg(byte[] toDecrypt, secret))

PHP and Android Keystore encryption / decryption

Ok, after searching and making sure the issue wasn't the public key stored in the PHP server, I found the answer. It was caused by the way to convert the "base64" string in an actual ByteArray in the App. This worked:

val token = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedToken))
val userID = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedUserId))

This is only working because I do the "base64_encode" in the server, for some (bad) reason I thought it was needed to go back to UTF8 to get the ByteArray in the app.

How can I encrypt the existing Room database with sqlcipher?

Encrypt each record of your database separately , you can use this package encrypt in a function that encrypt each record of your databsae.



Related Topics



Leave a reply



Submit