44

我正在关注本教程:如何使用 Android Keystore 存储密码和其他敏感信息。它(松散地)与 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 的更好方法?

非常感谢。

4

4 回答 4

63

我设法通过从 Cipher.getInstance 中删除 Provider 而不是强制转换为 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 及以下。

于 2015-09-10T10:10:16.657 回答
14

问题

  1. 我们正在尝试将“java.security.PrivateKey 解析java.security.interfaces.RSAPrivateKey ”和“java.security.PublicKeyjava.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
于 2017-02-06T10:30:39.123 回答
5

我还通过以下方法解决了这个问题(除了上面的@James 回答):在 Android 6.0 上,您不应该使用“AndroidOpenSSL”来创建密码,它会在密码初始化时因“需要 RSA 私钥或公钥”而失败以进行解密。只需使用 Cipher.getInstance("RSA/ECB/PKCS1Padding") 即可。

于 2016-08-10T20:35:17.010 回答
-4

我没有尝试过,但您应该能够将 android.security.keystore.AndroidKeyStoreRSAPrivateKey 分别转换为以下内容。这些应该是您需要的接口:

  1. java.security.PrivateKey
  2. java.security.interfaces.RSAKey
于 2015-09-04T15:52:56.027 回答