I looked for the past 2 days at possible solutions but couldn't find anything helpful. I have an Android app that needs to handle some encryption and decryption operations using Android KeyStore. Key generation and encryption seem to work fine, but I get some exceptions at decryption. More precisely an android.security.KeyStoreException is thrown with the explication: invalid input length. I will post below the code and the exceptions, and after that I'll describe what I have tried
Here is the key generation function:
private var provider =
fun generateKeys(): KeyPair {
val keyStore = KeyStore.getInstance(provider)
val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, provider);
val parmeterSpec = KeyGenParameterSpec.Builder(
ALIAS,
KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT
).run {
setUserAuthenticationRequired(false)
setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
// post (1) suggested this for a previous issue:
setRandomizedEncryptionRequired(true)
build()
}
kpg.initialize(parmeterSpec)
val kp = kpg.generateKeyPair()
return kp
}
Here is the encryption function:
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
fun encrypt(message: String, publicKey: PublicKey) : String? {
// MFG SHA1 used because of post (2)
val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)
val cipher = Cipher.getInstance(method)
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val encryptedBytes = cipher.doFinal(message.toByteArray())
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
}
The decrypt function:
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
fun decrypt(encrypted: String, privateKey: PrivateKey) : String? {
val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)
val cipher = Cipher.getInstance(method)
cipher.init(Cipher.DECRYPT_MODE, privateKey, spec)
val decryptedBytes = cipher.doFinal(encrypted.toByteArray())
return Base64.encodeToString(decryptedBytes, Base64.DEFAULT)
}
And at last here is an extract from which I call those functions:
val keyPair = generateKeys()
val message = "a simple message"
val encryptedMessage = encrypt(message, keyPair.public)
val result = decrypt(encryptedMessage, keyPair.private)
I also saw a this post suggesting that RSA cannot decrypt messages longer than 256. For me is not the case since in this case the encrypted message length is 175 bytes, and I'm not planning to use longer messages. Another suggestion I saw (forgot where) was that Base64 might add unwanted characters, and before decryption I should replace '\n'
with '' - but that doesn't work either... Is there something I am overlooking?
Here are the exception:
Caused by: javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:513)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
at com.sandwich.testkeystore.MainActivity.decrypt(MainActivity.kt:132)
at com.sandwich.testkeystore.MainActivity.onCreate(MainActivity.kt:65)
at android.app.Activity.performCreate(Activity.java:7822)
at android.app.Activity.performCreate(Activity.java:7811)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1328)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3452)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3620)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2183)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7604)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)
Caused by: android.security.KeyStoreException: Invalid input length
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1303)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:217)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
at com.sandwich.testkeystore.MainActivity.decrypt(MainActivity.kt:132)
at com.sandwich.testkeystore.MainActivity.onCreate(MainActivity.kt:65)
at android.app.Activity.performCreate(Activity.java:7822)
at android.app.Activity.performCreate(Activity.java:7811)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1328)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3452)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3620)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2183)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7604)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)