i am using the AndroidKeyStore to generate a RSA key pair, which are used to encrypt/decrypt the internal data.
The code which does that is as follows - it tries to retrieve the existing RSA key pair (via an alias ). If none exists then it tries to generate a new one. the code is as -
private void initializePublicPrivateKeys(){
try
{
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(APP_RSA_KEY_PAIR_SECRET_ALIAS, null);
_app_privateRSAKey = entry.getPrivateKey();
_app_publicRSAKey = entry.getCertificate().getPublicKey();
}
catch(Exception e){
}
}
private void initializeRSAKeyPairs() {
initializePublicPrivateKeys();
boolean isKeyNotGenerated = _app_privateRSAKey == null || _app_publicRSAKey == null;
if(isKeyNotGenerated)
{
//Check here, if we already stored some data with previous RSA key pair - if a entry is present in SharedPreference then that would mean we had previously generated a RSA key pair and the entry is in-turn encrypted by this key pair.
generateAppRSAPublicPrivateKeys();
initializePublicPrivateKeys();// initialize it again , since we have new keys generated.
}
}
@TargetApi(18)
private void generateAppRSAPublicPrivateKeys(){
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
// the certificate created would be valid for 25 years. This is just a random value.
cal.add(Calendar.YEAR, 25);
Date end = cal.getTime();
try{
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
Context appContext = getApplicationContext();
KeyPairGeneratorSpec.Builder keyPairGeneratorBuilder =
new KeyPairGeneratorSpec.Builder(appContext)
.setAlias("myrsaalias")
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal(String.format("CN=%s, OU=%s", "myrsaalias",
appContext.getApplicationInfo().packageName)));
if(Build.VERSION.SDK_INT >= 19){
keyPairGeneratorBuilder.setKeySize(2048);
}
generator.initialize(keyPairGeneratorBuilder.build());
generator.generateKeyPair();
}
catch(Exception e){
e.printStackTrace();
throw new IllegalArgumentException("Failed to generate RSA Public Private Key pair");
}
}
This code works fine. Once the key pair is generated, I use them to encrypt/decrypt data ( store this data in shared preference ) BUT after certain point of time ( after some app relaunch ) the initializePublicPrivateKeys function fails to retrieve the key pairs.(after this point it fails consistently ) and so what ever data that is stored in the shared preference in encrypted form is lost since i dont have the corresponding public key to decrypt that at all. ( if i generate a new one then, i guess, this would be different and would return incorrect results when i decrypt the data)
I was wondering in what cases this initializePublicPrivateKeys function can fail ?
PS: for now, I can't capture the exception details because the problem is occurring on some customer device which i don't have access to and I need to figure out the source of the problem myself. Also, device password or PIN is not changed during this period ( i confirmed this with my customer )
thanks in advance,
Is it possible the user changed their screen lock type to an insecure method such as None or Swipe thus triggering all the keys in the AndroidKeyStore to be deleted? It is a known issue discussed in this blog article: https://doridori.github.io/android-security-the-forgetful-keystore/
Also mentioned here: https://code.google.com/p/android/issues/detail?id=61989
If you do not have strict security requirements and want your app to work even if the user has no screen lock then perhaps just use a keypair stored in a keystore file in your data area and skip the
AndroidKeyStore
.