You can use a normal KeyStore file or the Android KeyStore Provider.
KeyStore (API 1)
You will have to create a KeyStore file and you will also have to manage the secret to access to it. This secret is very sensitive and difficult to hide from attackers. Personally I prefer to delegate that responsibility on the Android system, and this is why I don't choose this solution over the next one.
Android KeyStore Provider (API 18)
Using this API's you will delegate all the heavy lifting of managing the file and secret on Android. You will not need to use any password since the OS itself will store it deriving from your lock screen PIN code, password, pattern and other variables.
If the device contains an embedded secure hardware, the key will be stored there (e.g., Trusted Execution Environment (TEE)). You can check KeyInfo#isInsideSecureHardware() method to see whether the key is saved there or not. This hardware mechanism provide us an extra protection in case of our app is running into a compromised Linux kernel.
Moreover, beginning with Android 9 (API level 28), StrongBox Keymaster API was introduced for those devices which include a secure chip (like Titan M on Google Pixel 3). If you're running on Android 28 or above, you just need to invoke the setIsStrongBoxBacked(boolean) method to let Android know you want to use it if it's available on the device. Even though the aforementioned TEE solution is acceptable, this mechanism of using a Secure Element (SE) is the most secure one, since it is based on a different hardware (CPU, memory, storage, etc) designed for security purposes.
Here we would have a quick example of how to create a self signed certificate and a key pair using StrongBox if supported:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore")
.apply {
val certBuilder = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT)
.setKeyValidityStart(keyValidityStart)
.setKeyValidityEnd(keyValidityEnd)
.setCertificateSerialNumber(BigInteger.valueOf(1L))
.setCertificateSubject(X500Principal("CN=MyCompany"))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
initialize(
certBuilder
.setIsStrongBoxBacked(true) /* Enable StrongBox */
.build()
)
} else {
initialize(certBuilder.build())
}
}
.also {
val keyPair = it.generateKeyPair()
...
}
}
Regarding to your questions:
Can we extract the key from that KeyStore?
With both of them your app can get the PrivateKey and use it if this is what you mean.
Can another application (AppB) access the key generated by AppA?
With the KeyStore provider solution each app can only access to their KeyStore instances or aliases. Instead, using a normal KeyStore if another app has access to the file it could try to attack it.
In which case we may loose the key? Device Factory reset only?
Deleting the app will erase the KeyStore provider instance of your app. However, due to a nasty bug of Android (more common before Android 5) if the user changes the lock screen pattern into a password or just deletes the pattern, the KeyStore will be fully corrupted. The same happens when the user performs a "Clear credentials" from the Android settings. With the classic KeyStore instead, you could store it in the external storage and keep it even after a device factory reset. However, as mentioned before, it is still less secure than the provider.
I may be wrong in some aspects, but this is what I learn and I just shared my experience. Hope this is helpful for you.