40

I want to know how to securely store encryption key in Android? What is the best scenario to protect encryption and secrete keys?

itMaxence
  • 1,230
  • 16
  • 28
  • 2
    Are you taking about encrypting something on disk, or an encrypted file being stored with your app? In the first case, create the key at runtime and put it in the Keystore. In the second case- do not put the key in the app. The best thing to do is download it from the server, but doing so still leaves a window for intercept. Really both do, if someone is trying hard enough they will get it. The best thing to do is not put any information on the client phone you don't want the user to be able to see. – Gabe Sechan Oct 05 '17 at 06:00
  • please give me an example of storing key in keystore –  Oct 05 '17 at 06:07
  • Are you asking about encryption keys to send data to the server or other users (then you can store safely an assymetric public key in your apk or download it), a local encryption key to encrypt/decrypt data in your device, or do you want to encrypt the communication channel with your server? – pedrofb May 17 '18 at 09:17
  • I'm asking abut local data encryption key. –  May 17 '18 at 09:18
  • 1
    Then use [Android Keystore](https://developer.android.com/training/articles/keystore). It is designed for this purpose. What are you looking for with your question? a tutorial? some examples? – pedrofb May 17 '18 at 09:21
  • Android Keystore will not work below API level 18. –  May 17 '18 at 09:24
  • I suggest to include this relevant info in your question. For Android <18, are you worried about root level access to the device? No>Generate a random encryption key and store it in SharedPreferencences. Yes> Request a password to the user, derive a encryption key from the password, and store it in SharedPreferences. Drawback: you need to prompt for the password when application starts – pedrofb May 17 '18 at 09:35
  • @pedrofb, SharedPreferences can be access by root privilege user. –  May 17 '18 at 09:38
  • Sorry, I have written too fast. In the second case, the encryption key does not need to be stored in the SharedPreferences because it is calculated each time when the application is started using the password – pedrofb May 17 '18 at 09:48
  • According to your second case, we need to store password instead of an encryption key. So, raised the same question: **How to securely store password?** –  May 17 '18 at 10:07
  • The password is not stored. It is requested to user each time the application starts. Nobody can derive the real encryption key because the password it is only known by the user – pedrofb May 17 '18 at 10:12

5 Answers5

51

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

pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Thanks for the sharing useful android cryptography information. –  May 17 '18 at 10:33
  • Thank you for the exhaustive answer! Speaking about API Level ">=18< 23" can we avoid storing AES key in SharedPreferences by simply using RSA Private Key part as a symmetric key? – Kurovsky Dec 28 '18 at 09:28
  • 1
    @southerton, I think you can get a sufficiently secure AES secret applying a key derivation function like HKDF to the RSA key or just hashing it (SHA256 will provide 32 random bytes). Check out the discussion of this post https://crypto.stackexchange.com/questions/50118/derive-a-secret-from-an-rsa-private-key – pedrofb Dec 28 '18 at 21:50
  • @pedrofb looking at the documentation https://developer.android.com/training/articles/keystore#SupportedCiphers we can see that "AES/CBC/PKCS7Padding" is not supported below API 23, then how can we use it below API 23? – Rahul Sahni May 25 '20 at 14:33
  • @rahulsahni, it is not supported by `AndroidKeyStore` provider, but you can still use AES with the default cryptographic provider – pedrofb May 25 '20 at 15:15
  • hi. why you are generating AES key below 23? you can encyrpt password with public key and decrpyt with private key directly . Is it not safe to use rsa keys directly? – ahmetvefa53 Mar 10 '21 at 13:34
  • @ahmetvefa53,because RSA encryption is limited by the size of the key. An AES key is generated for symmetric encryption, which is also much faster, and the AES key is encrypted with the RSA – pedrofb Mar 10 '21 at 13:47
  • @pedrofb I could not understand that "RSA encryption is limited by the size of the key"? – ahmetvefa53 Mar 10 '21 at 14:06
  • my password configuration is max 10 chars.Can ı use rsa directly? – ahmetvefa53 Mar 10 '21 at 14:09
  • See https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security _With the commonly used "v1.5 padding" and a 2048-bit RSA key, the maximum size of data which can be encrypted with RSA is 245 bytes. No more._ RSA is enough for 10 characters – pedrofb Mar 10 '21 at 14:27
4

It sounds like you want EncryptedSharedPreferences or EncryptedFile. Both of these use the AndroidKeyStore. The code snippets below actually answer the question "How do I use the AndroidKeystore to encrypt a file or store a cryptographic key?"

Make sure to include implementation "androidx.security:security-crypto:1.0.0-rc02" in your app build.gradle file.

from EncryptedSharedPreferences:

An implementation of SharedPreferences that encrypts keys and values.

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
  "secret_shared_prefs",
  masterKeyAlias,
  context,
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

You could store an encryption key like this:

// generate random symmetric key
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey symkey = keyGen.generateKey();


String alias = "your encryption key";

// store symmetric key
byte[] encodedSymmetricKey = symkey.getEncoded();
SharedPreferences.Editor edit = sharedPreferences.edit();
String base64EncodedSymmetricKey = new String(Base64.getEncoder().encode(encodedSymmetricKey));
edit.putString(alias, base64EncodedSymmetricKey);
edit.commit();

// retrieve symmetric key
String raw = sharedPreferences.getString(alias, null);
byte[] symKey = Base64.getDecoder().decode(raw);
SecretKeySpec spec = new SecretKeySpec(symKey, "AES");

assert(spec.equals(symkey));

// use your encryption key

Although it would be much better to use EncryptedFile.


from EncryptedFile:

Class used to create and read encrypted files.

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

File file = new File(context.getFilesDir(), "secret_data");
EncryptedFile encryptedFile = EncryptedFile.Builder(
  file,
  context,
  masterKeyAlias,
  EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build();

// write to the encrypted file
FileOutputStream encryptedOutputStream = encryptedFile.openFileOutput();

// read the encrypted file
FileInputStream encryptedInputStream = encryptedFile.openFileInput();

note:
The master key, once created, is constant. So even after a phone reboot your app will still be able to encrypt/decrypt the file.

silverduck
  • 401
  • 6
  • 9
3

You cannot place the encryption key inside your apk file. You may want to keep it in a remote server and decrypt using server. Or you may make it difficult for others by encoding the key and keeping it in non-obvious places. But there's no bullet proof solution for this.

Nabin Bhandari
  • 15,949
  • 6
  • 45
  • 59
  • what about **androidKeyStore** ? –  Oct 05 '17 at 06:01
  • 1
    If the encryption key can be a random String generated at runtime, you can use KeyStore. But it can be read from a device with root access if stored persistently. – Nabin Bhandari Oct 05 '17 at 06:08
2

There is no way to securely save your private api keys into code. But you can use NDK to securely save private keys. It is not trivial to get key from NDK. Secue Key With NDK Example

Manish
  • 1,071
  • 2
  • 10
  • 27
  • 4
    C++ on the other hand can’t be decompiled but can be **disassembled**, which is slightly less trivial than java. its also not secure :( –  Oct 05 '17 at 06:16
0

You can use Android Keystore system to store and retrieve sensitive information. Read this 5 minute article to understand how it works. Using the Android Keystore system to store and retrieve sensitive information

Neeraj
  • 499
  • 8
  • 16
  • 1
    Where I can save the IV ? – Bytecode Sep 26 '18 at 18:09
  • As far as my experience, you need to have the same IV to decrypt. So, when encryption does, you will have IV, convert it to Base64 encoding and attach with the encrypted data by putting some separator and store your final encrypted text, So when you decrypt, you will have Base64 encoded IV attached with the text you encrypted, split that with the separator you put during encryption and you will get IV in Base64 encoded form, decode it and use for decryption. – Amir Raza Feb 22 '21 at 17:15