how to securely store encryption keys in android?

18,547

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 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.

Share:
18,547
Admin
Author by

Admin

Updated on June 18, 2022

Comments

  • Admin
    Admin almost 2 years

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

  • Admin
    Admin over 6 years
    what about androidKeyStore ?
  • Nabin Bhandari
    Nabin Bhandari over 6 years
    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.
  • Admin
    Admin over 6 years
    C++ on the other hand can’t be decompiled but can be disassembled, which is slightly less trivial than java. its also not secure :(
  • Admin
    Admin almost 6 years
    Thanks for the sharing useful android cryptography information.
  • Bytecode
    Bytecode over 5 years
    Where I can save the IV ?
  • southerton
    southerton over 5 years
    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?
  • pedrofb
    pedrofb over 5 years
    @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 crypto.stackexchange.com/questions/50118/…
  • Rahul Sahni
    Rahul Sahni almost 4 years
    @pedrofb looking at the documentation developer.android.com/training/articles/… we can see that "AES/CBC/PKCS7Padding" is not supported below API 23, then how can we use it below API 23?
  • pedrofb
    pedrofb almost 4 years
    @rahulsahni, it is not supported by AndroidKeyStore provider, but you can still use AES with the default cryptographic provider
  • Amir Raza
    Amir Raza about 3 years
    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.
  • ahmetvefa53
    ahmetvefa53 about 3 years
    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?
  • pedrofb
    pedrofb about 3 years
    @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
  • ahmetvefa53
    ahmetvefa53 about 3 years
    @pedrofb I could not understand that "RSA encryption is limited by the size of the key"?
  • ahmetvefa53
    ahmetvefa53 about 3 years
    my password configuration is max 10 chars.Can ı use rsa directly?
  • pedrofb
    pedrofb about 3 years
    See security.stackexchange.com/questions/33434/… 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