Android Keystore getEntry() and generateKeyPair() throw Exceptions sometimes
Asked Answered
T

1

19

My Android app need to encrypt a file so that it can decrypt and read it later. This should not be decrypt-able by anybody else other than the app, even user.

Following is how I am doing the encryption and decryption. This works most of the time, but some times for some users this is failing. It is not specific to a particular handset (Nexus7, Samsung, Motorola, HTC -- all types are reporting this issue), but not all users are experiencing it. Only some users occasionally.

Here is the relevant code:

encrypt() {
   KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
   final KeyStore.PrivateKeyEntry entry;
   if (!ks.containsAlias(CERT_ALIAS)) {
       Calendar cal = Calendar.getInstance();
       Date now = cal.getTime();
       cal.add(Calendar.YEAR, 50);
       Date end = cal.getTime();
       KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
       kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
              .setAlias(CERT_ALIAS)
              .setStartDate(now)
              .setEndDate(end)
              .setSerialNumber(BigInteger.valueOf(1))
              .setSubject(new X500Principal("CN=" + CERT_ALIAS))
              .build());
       KeyPair kp = kpg.generateKeyPair();
   }
   entry = (KeyStore.PrivateKeyEntry) ks.getEntry(
                     CERT_ALIAS, null);
   pub = entry.getCertificate().getPublicKey();
   // use the pub key to encrypt
}
decrypt() {
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);

    final KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) ks.getEntry(
            CERT_ALIAS, null);
    PrivateKey key1 = entry.getPrivateKey();
    // use the private key decrypt
}

This code sometimes throws

java.lang.RuntimeException: error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long
at com.android.org.conscrypt.NativeCrypto.ENGINE_load_private_key(Native Method)
at com.android.org.conscrypt.OpenSSLEngine.getPrivateKeyById(OpenSSLEngine.java:66)
at android.security.AndroidKeyStore.engineGetKey(AndroidKeyStore.java:86)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:372)
at java.security.KeyStore.getEntry(KeyStore.java:644)

So I modified encrypt() to first try to get the entry and if it raises exception, generate new key pair.

final KeyStore.PrivateKeyEntry entry = null;
if (ks.containsAlias(CERT_ALIAS)) {
    try {
        entry = (KeyStore.PrivateKeyEntry) ks.getEntry(
                      CERT_ALIAS, null);
    } catch (Exception e) {
    }
}
if (entry == null) {
    //generate new key pair
}

But even this is failing sometimes with the following exception.

java.lang.IllegalStateException: could not generate key in keystore
at android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:100)
at java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:275)
  1. What am I doing wrong?
  2. How do I fix it/work around it?
  3. Does these exceptions indicate that the files are being tampered with?
  4. Does this happen for users with screenlock password/pin?
  5. Before I generate new pair, should I delete the entry? (KeyStore.deleteEntry())

I observed that the keystore returns null after screenlock password/pin change. Some others also seem to have experienced this issue (KeyStore getEntry return null after change password)

Torero answered 31/5, 2014 at 0:40 Comment(3)
Hello Kakatiyudu, do you have found any work around to this issue from the time you posted this question, because I'm faced to this issue too, but, so far, no solution on my side ...Toffeenosed
Nope. If I get any such error, I am falling back to some other less secure way (the method I use for older devices anyway)Torero
Kakatiyudu, did you find a workaround?Basia
E
7

I ran into the could not generate key in keystore issue with one of my apps and after digging deep into it with one of the affected phones I discovered that some devices have it set up that the phone's unlock pattern/pass/pin is different than the password that actually unlocks the key storage. If you want to double check that that is your issue as well you can use the work here: http://nelenkov.blogspot.com/2012/05/storing-application-secrets-in-androids.html to get at the actual private system api the public KeyPairGenerator objects are calling and check the return code on it. I'm not sure why Google decided to hide the return code behind a boolean but there you have it.

You can trigger an unlock of the keystore manually by calling startActivity(new Intent("com.android.credentials.UNLOCK")); but this might not really help much. From what I've seen if a phone is in this state it is because some Device Administrator app locked the keystore in the background so it can set up VPN or email credentials. This means the user doesn't actually know the password. I'm still looking into a workaround (possibly find out how the Device Administrator apps get access to the keystore so I can unlock it that way) but it's a hairy issue to say the least. I'll try to update this if I find out more in my explorations, hopefully this at least points some people in the right direction.

Electronarcosis answered 8/4, 2015 at 0:3 Comment(2)
Hans, did you find a workaround? Do you know how to reproduce the "java.lang.IllegalStateException: could not generate key in keystore" issue?Basia
The same qs: any workaround? Sometimes after an app updates this one simply doesn't work: keyStore.getEntryVambrace

© 2022 - 2024 — McMap. All rights reserved.