Reputation: 153
I cannot decrypt previously encrypted String on Android. Problem occurs mainly on Sony devices (Xperia Z5 and Xperia Z5 Compact) running Android 6 Marshmallow.
android.security.KeyStoreException: Incompatible purpose
is thrown when last line is executed (where alias is name for stored key).
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKey);
The KeyStore itself is obtained by
KeyStore.getInstance("AndroidKeyStore");
And key is generated by the following method:
private static void createKey(String alias, String subject, KeyStore keyStore, BigInteger serialNumber, Date startDate, Date endDate, String algorithm, String keyStoreProvider, Context context)
throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (keyStore.containsAlias(alias)) {
// Key already exists.
return;
}
// Generate keys.
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal(subject))
.setSerialNumber(serialNumber)
.setStartDate(startDate)
.setEndDate(endDate)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm, keyStoreProvider);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
Where algorithm is "RSA" and keyStoreProvider is "AndroidKeyStore".
The part of the stack trace:
android.security.KeyStoreException: Incompatible purpose
at android.security.KeyStore.getKeyStoreException(KeyStore.java:636)
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:716)
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:53)
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:263)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:108)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:612)
at javax.crypto.Cipher.tryCombinations(Cipher.java:532)
at javax.crypto.Cipher.getSpi(Cipher.java:437)
at javax.crypto.Cipher.init(Cipher.java:815)
at javax.crypto.Cipher.init(Cipher.java:774)
The exception causes java.security.InvalidKeyException: Keystore operation failed to be thrown.
I was not able to reproduce the error directly on my device (the creash information are from Crashlytics).
Following the stacktrace and code of KeyStore: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/keystore/java/android/security/KeyStore.java
It seems that problem is with Keymaster layer.
Upvotes: 5
Views: 5488
Reputation: 35232
In the Android Keystore System, purpose
references how a key can be used. Possible options for a asymmetric key are:
The updated Android Keystore System API for SDK 23 does let you limit the propose for your key (see KeyProperties
).
An example with the new API:
KeyGenParameterSpec.Builder keyGenParameterSpecBuilder = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(algorithmDefinition.keyLengthBit, algorithmDefinition.publicExponent))
.setRandomizedEncryptionRequired(meta.randomizedEncryptionRequired)
.setBlockModes(algorithmDefinition.blockMode)
.setUserAuthenticationRequired(meta.userAuthenticationRequired)
.setKeySize(algorithmDefinition.keyLengthBit);
Maybe there is a device bug that this device runs SDK 23 and you use the old API, but the device requires you to use the new API, setting the prupose to what you want to do?
Upvotes: 4