java - Crash casting AndroidKeyStoreRSAPrivateKey a RSAPrivateKey
cryptography java-security (4)
Estoy siguiendo este tutorial: Cómo usar el Android Keystore para almacenar contraseñas y otra información confidencial . Se relaciona (sin apretar) con la aplicación Google Sample: BasicAndroidKeyStore .
Puedo encriptar mis datos usando la clave pública, y puedo descifrar en dispositivos que ejecutan Lollipop. Sin embargo, tengo un Nexus 6 corriendo malvavisco y este se bloquea al dar el error:
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
Aquí está el código en el que falla:
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);
Soy reacio a atribuir esto a una rareza de Android ya que no veo ninguna razón por la que las bibliotecas de cifrado de java habrían cambiado. Si aparece el lanzamiento M y nuestra aplicación se bloquea inmediatamente en M, tendré grandes problemas.
Estoy haciendo algo mal? El error dice muy específicamente que no se puede convertir a RSAPrivateKey, por lo que ¿alguien sabe una mejor manera de obtener el RSAPrivateKey de la entrada?
Muchas muchas gracias.
Logré hacer que esto funcionara al eliminar el proveedor de Cipher.getInstance y no convertirlo a una RSAprivateKey.
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
No estoy al 100%, pero creo que la razón de esto es el cambio en marshmallow de OpenSSL a BoringSSL. https://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client
De todos modos, lo anterior funcionó para M y abajo.
No lo he probado pero deberías poder lanzar el android.security.keystore.AndroidKeyStoreRSAPrivateKey a lo siguiente por separado. Estas deberían ser las interfaces que necesita:
- java.security.PrivateKey
- java.security.interfaces.RSAKey
Resolví este problema al seguir esto (aparte de la respuesta de @James anterior): en Android 6.0 no debe usar "Android OpenSSL" para la creación de cifrado, fallaría con "Necesidad de clave RSA privada o pública" en cifrado init para descifrado. Simplemente use Cipher.getInstance ("RSA / ECB / PKCS1Padding") y funcionará.
Problema
- Estamos intentando analizar "java.security. PrivateKey para java.security.interfaces. RSAPrivateKey " y "java.security. PublicKey para java.security.interfaces. RSAPublicKey ". Es por eso que estamos obteniendo ClassCastException.
Solución
- No es necesario analizar la clave, podemos usar directamente "java.security. PrivateKey " y "java.security. PublicKey " para cifrado y descifrado.
Cifrado
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry;
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey(); // Don''t TypeCast to RSAPublicKey
Descifrado
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)entry;
PrivateKey privateKey = privateKeyEntry.getPrivateKey(); // Don''t TypeCast to RSAPrivateKey