将 AndroidKeyStoreRSAPrivateKey 转换为 RSAPrivateKey 时发生崩溃
Crash casting AndroidKeyStoreRSAPrivateKey to RSAPrivateKey
我正在学习本教程:How to use the Android Keystore to store passwords and other sensitive information. It (loosely) ties up with the Google Sample app: BasicAndroidKeyStore。
我可以使用 public 密钥加密我的数据,我可以在设备 运行 Lollipop 上解密。但是我有一个 Nexus 6 运行 marshmallow 并且它崩溃并给出错误:
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 的更好方法吗?
非常感谢。
我没试过,但你应该可以将 android.security.keystore.AndroidKeyStoreRSAPrivateKey 分别转换为以下内容。这些应该是您需要的接口:
- java.security.PrivateKey
- java.security.interfaces.RSAKey
我设法通过从 Cipher.getInstance 中删除 Provider 并 not 转换为 RSAprivateKey 来实现此功能。
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
我不是 100%,但我认为这是因为 marshmallow 从 OpenSSL 到 BoringSSL 的变化。
https://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client
总之,以上方法适用于 M 及以下。
我也按照以下方法解决了这个问题(除了上面的@James 回答):
在 Android 6.0 上,您不应使用 "AndroidOpenSSL" 进行密码创建,它会在密码初始化时使用 "Need RSA private or public key" 失败以进行解密。只需使用 Cipher.getInstance("RSA/ECB/PKCS1Padding") 即可。
问题
- 我们正在尝试解析 "java.security.PrivateKey to java.security.interfaces.RSAPrivateKey" & "java.security.PublicKey to java.security.interfaces.RSAPublicKey"。这就是我们得到 ClassCastException 的原因。
解决方案
- 不需要解析密钥,直接使用"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
我正在学习本教程:How to use the Android Keystore to store passwords and other sensitive information. It (loosely) ties up with the Google Sample app: BasicAndroidKeyStore。
我可以使用 public 密钥加密我的数据,我可以在设备 运行 Lollipop 上解密。但是我有一个 Nexus 6 运行 marshmallow 并且它崩溃并给出错误:
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 的更好方法吗?
非常感谢。
我没试过,但你应该可以将 android.security.keystore.AndroidKeyStoreRSAPrivateKey 分别转换为以下内容。这些应该是您需要的接口:
- java.security.PrivateKey
- java.security.interfaces.RSAKey
我设法通过从 Cipher.getInstance 中删除 Provider 并 not 转换为 RSAprivateKey 来实现此功能。
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
我不是 100%,但我认为这是因为 marshmallow 从 OpenSSL 到 BoringSSL 的变化。 https://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client
总之,以上方法适用于 M 及以下。
我也按照以下方法解决了这个问题(除了上面的@James 回答): 在 Android 6.0 上,您不应使用 "AndroidOpenSSL" 进行密码创建,它会在密码初始化时使用 "Need RSA private or public key" 失败以进行解密。只需使用 Cipher.getInstance("RSA/ECB/PKCS1Padding") 即可。
问题
- 我们正在尝试解析 "java.security.PrivateKey to java.security.interfaces.RSAPrivateKey" & "java.security.PublicKey to java.security.interfaces.RSAPublicKey"。这就是我们得到 ClassCastException 的原因。
解决方案
- 不需要解析密钥,直接使用"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