how to securely store encryption keys in android?
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.
Admin
Updated on June 18, 2022Comments
-
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 over 6 yearswhat about androidKeyStore ?
-
Nabin Bhandari over 6 yearsIf 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 over 6 yearsC++ on the other hand can’t be decompiled but can be disassembled, which is slightly less trivial than java. its also not secure :(
-
Admin almost 6 yearsThanks for the sharing useful android cryptography information.
-
Bytecode over 5 yearsWhere I can save the IV ?
-
southerton over 5 yearsThank 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 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 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 almost 4 years@rahulsahni, it is not supported by
AndroidKeyStore
provider, but you can still use AES with the default cryptographic provider -
Amir Raza about 3 yearsAs 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 about 3 yearshi. 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 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 about 3 years@pedrofb I could not understand that "RSA encryption is limited by the size of the key"?
-
ahmetvefa53 about 3 yearsmy password configuration is max 10 chars.Can ı use rsa directly?
-
pedrofb about 3 yearsSee 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