UnrecoverableKeyException Не удалось получить информацию о закрытом ключе, KeyStoreException: Неверный большой двоичный объект ключа

В нашем приложении у нас были проблемы с данными в хранилище ключей Android, которые внезапно становились недоступными. Конкретное исключение, которое мы видим, здесь:

java.security.UnrecoverableKeyException: Failed to obtain information about private key
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:223)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(AndroidKeyStoreProvider.java:259)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(AndroidKeyStoreProvider.java:269)
 at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:94)
 at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:474)
 at java.security.KeyStore.getEntry(KeyStore.java:1560)
 at <PACKAGE_NAME>.EncryptionInteractor.generateKeys(EncryptionInteractor.java:104)
 at <PACKAGE_NAME>.EncryptionInteractor.generateKeys(EncryptionInteractor.java:100)
 at <PACKAGE_NAME>.EncryptionInteractor.init(EncryptionInteractor.java:93)
 at <PACKAGE_NAME>.EncryptionInteractor.<init>(EncryptionInteractor.java:80)
 at <PACKAGE_NAME>.EncryptionInteractor.init(EncryptionInteractor.java:65)
 at <PACKAGE_NAME>.<APPLICATION_CLASS>.onCreate(APPLICATION_CLASS.java:17)
 at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1118)
 at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5791)
 at android.app.ActivityThread.-wrap1(Unknown Source:0)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1661)
 at android.os.Handler.dispatchMessage(Handler.java:105)
 at android.os.Looper.loop(Looper.java:164)
 at android.app.ActivityThread.main(ActivityThread.java:6541)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: android.security.KeyStoreException: Invalid key blob
 at android.security.KeyStore.getKeyStoreException(KeyStore.java:695)
 at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:224)
  ... 21 more

Нам не удалось придумать надежный способ воспроизвести проблему. В нескольких статьях упоминаются возможные состояния, которые могут привести к тому, что хранилище ключей «забудет» ключ или заблокируется, например здесь. Однако, насколько я могу судить, мы не попали ни в один из этих крайних случаев. Кажется, это происходит после того, как устройство некоторое время постоит после первой настройки ключа. Мы видели, как это происходит на нескольких эмуляторах и устройствах, от 21 до 26. Кроме того, эти устройства использовали либо пролистывание для разблокировки, либо PIN-код. Изменение PIN-кода или метода безопасности, похоже, не вызывает проблемы. Опять же, эта проблема возникает после того, как устройство не использовалось в течение нескольких дней.

Я нашел два других SO здесь и здесь, а также один Google проблема. Если я правильно понимаю, ответ, связанный с обоими, похоже, основан на предположении, что вызывающий абонент вызвал setUserAuthenticationValidityDurationSeconds при создании ключа, а мы этого не сделали. Кроме того, данное решение, по-видимому, основано на простом удалении ключа и создании нового.

Ниже показана наша настройка ключа для версий >= API 23. Я не упомянул нашу генерацию ключа для версий старше 23, поскольку мы в основном видели это на API >= 23.

private static final int RSA_KEY_SIZE = 2048;
private static final String CERT_SUBJECT_STRING = "CN=<COMPANY_NAME> Android App O=<COMPANY_NAME>";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";

try {
        String alias = KEY_NAME;
        KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);

        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        end.add(Calendar.YEAR, 1);
        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(RSA_KEY_SIZE, RSAKeyGenParameterSpec.F4))
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
            .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
            .setCertificateNotAfter(end.getTime())
            .setCertificateNotBefore(start.getTime())
            .setCertificateSerialNumber(BigInteger.ONE)
            .setCertificateSubject(new X500Principal(CERT_SUBJECT_STRING))
            .build();
        generator.initialize(spec);
        generator.generateKeyPair();
    } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }

Затем мы пытаемся получить доступ к ключам позже через keyStore.getEntry(KEY_NAME, null). Опять же, это работает какое-то время, но затем начинает выдавать указанное выше исключение.


person jinaud    schedule 16.01.2018    source источник
comment
вы нашли какое-нибудь решение?   -  person David Miguel    schedule 11.04.2018


Ответы (1)


Я также столкнулся с проблемами стабильности в KeyStore.

Решение заключалось в использовании для закрытый ключ

PrivateKey privKey = ks.getKey(alias, password)

И это для открытого ключа

PublicKey pubKey = ks.getCertificate(alias).getPublicKey();

вместо getEntry

ks.getEntry(alias, password)

Проблема была не в том, как вы создаете ключ, а в том, как вы его читаете.

Больше года с этой проблемой не сталкивался.

person devzeze    schedule 11.10.2019