Сбой приведения AndroidKeyStoreRSAPrivateKey к RSAPrivateKey

Я следую этому руководству: Как использовать Android Хранилище ключей для хранения паролей и другой конфиденциальной информации. Оно (условно) связано с примером приложения Google: BasicAndroidKeyStore.

Я могу зашифровать свои данные с помощью открытого ключа и расшифровать на устройствах с Lollipop. Однако у меня есть Nexus 6, работающий с зефиром, и он вылетает, выдавая ошибку:

java.lang.RuntimeException: Unable to create application com.android.test: java.lang.ClassCastException: android.security.keystore.AndroidKeyStoreRSAPrivateKey cannot be cast to java.security.interfaces.RSAPrivateKey

Вот код, на котором происходит сбой:

KeyStore.Entry entry;

//Get Android KeyStore
ks = KeyStore.getInstance(KeystoreHelper.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);

// Weird artifact of Java API.  If you don't have an InputStream to load, you still need to call "load", or it'll crash.
ks.load(null);

// Load the key pair from the Android Key Store
entry = ks.getEntry(mAlias, null);

KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;

//ERROR OCCURS HERE::
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();

Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");

output.init(Cipher.DECRYPT_MODE, rsaPrivateKey);

Я не хочу списывать это на странность Android M, поскольку не вижу причин, по которым криптографические библиотеки Java изменились бы. Если появится версия M, и наше приложение сразу же выйдет из строя на M, у меня будут большие проблемы.

Я делаю что-то не так? В ошибке очень конкретно говорится, что вы не можете выполнить преобразование в RSAPrivateKey, поэтому кто-нибудь знает лучший способ получить RSAPrivateKey из записи?

Большое большое спасибо.


person James    schedule 04.09.2015    source источник


Ответы (4)


Мне удалось заставить это работать, удалив Provider из Cipher.getInstance и не приведя к RSAprivateKey.

KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;

Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());

Я не на 100%, но я думаю, что причиной этого, я считаю, является изменение зефира с OpenSSL на BoringSSL. https://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client

Во всяком случае, вышеизложенное работало для M и ниже.

person James    schedule 10.09.2015
comment
Теперь откройте на AOSP: Проблема 205450: сбой приведения AndroidKeyStoreRSAPrivateKey к RSAPrivateKey. - person jww; 30.03.2016
comment
это решение работает на Lollipop. Но на зефире он выдает: java.security.InvalidKeyException: нужен закрытый или открытый ключ RSA - person deviant; 05.08.2016
comment
@ resp78 resp78 В Android 6.0 вам не следует использовать AndroidOpenSSL для создания шифра, это приведет к сбою из-за необходимости частного или открытого ключа RSA при инициализации шифра для расшифровки. Просто используйте Cipher.getInstance(RSA/ECB/PKCS1Padding), и все заработает. - person Sid; 10.08.2016
comment
Работает на Marshmallow и Android N preview 5. Спасибо. - person Misagh Emamverdi; 16.08.2016
comment
Вашего решения недостаточно, чтобы предотвратить его сбой на Andr 6. Я попробовал его и сделал еще несколько вещей, это установить encryptPadding: setEncryptionPaddings (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) заменить на ENCRYPTION_PADDING_NONE, и тогда это сработало. - person Khang Tran; 15.12.2016
comment
Спасибо!! это сработало для меня на Nougat. Я использовал AndroidOpenSSL на Cipher getInstance. - person Hermandroid; 09.02.2017
comment
Решение сработало для меня. В Android 6.0+ privateKeyEntry.getPrivateKey() будет возвращать объект AndroidKeyStoreRSAPrivateKey вместо объекта RSAPrivateKey с шифром, инициализированным с помощью RSA/ECB/PKCS1Padding. Если вы укажете AndroidOpenSSL в качестве поставщика для расшифровки, старый класс OpenSSLCipherRSA будет использоваться для обработки приватного, но он не поддерживает AndroidKeyStoreRSAPrivateKey. Поэтому возникает исключение. Если вы используете поставщика по умолчанию для расшифровки, будет использоваться AndroidKeyStoreRSACipherSpi.PKCS1Padding, который поддерживает AndroidKeyStoreRSAPrivateKey. - person Keeeeeenw; 04.05.2017
comment
Обратите внимание, что у меня была эта проблема как на устройстве Nexus/Marshmallow, так и на устройстве Sony/Oreo (но не на некоторых других). Снятие гипса зафиксировало оба устройства. - person Andy Krouwel; 02.01.2018
comment
Класс AndroidKeyStoreRSAPrivateKey написан неправильно и должен быть производным от RSAPrivateKey, чтобы быть совместимым с безопасным дополнением. Это ошибка Android, а не ваш код. Вы можете исправить это, создав класс-оболочку, производный от RSAPrivateKey, и перенаправив ему методы. - person Erik Aronesty; 03.01.2018

Проблема

  1. Мы пытаемся разобрать "java.security.PrivateKey на java.security.interfaces.RSAPrivateKey" и "java.security.PublicKey на java. .security.interfaces.RSAPublicKey". Вот почему мы получаем ClassCastException.

Решение

  1. Нам не нужно анализировать ключ, мы можем напрямую использовать «java.security.PrivateKey» и «java.security.PublicKey» для шифрования и дешифрования.

Шифрование

KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry; 
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey(); // Don't TypeCast to RSAPublicKey

Расшифровка

KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry;
PrivateKey privateKey = privateKeyEntry.getPrivateKey(); // Don't TypeCast to RSAPrivateKey
person Vasanth    schedule 06.02.2017
comment
Выдает исключение с сообщением: нужен закрытый или открытый ключ RSA. - person Talha; 20.01.2018

Я решил эту проблему, также следуя этому (помимо ответа @James выше): в Android 6.0 вы не должны использовать «AndroidOpenSSL» для создания шифра, это приведет к сбою с «Нужен закрытый или открытый ключ RSA» при инициализации шифрования для расшифровки. Просто используйте Cipher.getInstance("RSA/ECB/PKCS1Padding") и все заработает.

person Sid    schedule 10.08.2016
comment
какого провайдера тогда использовать? - person Jonathan; 16.12.2016

Я не пробовал, но вы должны иметь возможность приводить android.security.keystore.AndroidKeyStoreRSAPrivateKey к следующему отдельно. Это должны быть интерфейсы, которые вам нужны:

  1. java.security.PrivateKey
  2. java.security.interfaces.RSAKey
person James    schedule 04.09.2015