I am trying to encrypt and decrypt data by RSA with Android. this is my code for encrypt/decrypt
fun encryptStringRSA(str: String, publicKey: PublicKey): String{
val encryptedBytes: ByteArray? = Cipher.getInstance("RSA/NONE/PKCS1Padding").run {
init(Cipher.ENCRYPT_MODE, publicKey)
doFinal(str.toByteArray(Charsets.UTF_8))
}
return Base64.encodeToString(encryptedBytes, EncryptionConstants.BASE_64_OPTIONS)
}
fun decryptStringRSA(str: String, privateKey: PrivateKey): String{
val decryptedBytes: ByteArray = Cipher.getInstance("RSA/NONE/PKCS1Padding").run {
init(Cipher.DECRYPT_MODE, privateKey)
doFinal(Base64.decode(str, EncryptionConstants.BASE_64_OPTIONS))
}
return String(decryptedBytes)
}
And it works great when I use keys generated by Node JS server(this was for sync algorithms parameters), However now I need to generate keys on Android side and this is how I do that:
fun generateRSAKeyPair(keyAlias: String): KeyPair {
val startDate = GregorianCalendar()
val endDate = GregorianCalendar()
endDate.add(Calendar.YEAR, 1)
val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE)
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(keyAlias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY or KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT).run {
setDigests(KeyProperties.DIGEST_SHA256)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
setKeySize(2048)
setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
setCertificateNotBefore(startDate.time)
setCertificateNotAfter(endDate.time)
setUserAuthenticationRequired(true)
setUserAuthenticationValidityDurationSeconds(30)
build()
}
keyPairGenerator.initialize(parameterSpec)
val keyPair = keyPairGenerator.genKeyPair()
val publicKey = getPublicKey(
Base64.encodeToString(
keyPair.public.encoded,
EncryptionConstants.BASE_64_OPTIONS
)
)
return KeyPair(publicKey, keyPair.private)
}
And then get from Keystore:
fun initKeyPair(keyAlias: String): KeyPair{
try {
val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply {
load(null)
}
val entry: KeyStore.Entry = keyStore.getEntry(keyAlias, null)
val privateKey: PrivateKey? = (entry as KeyStore.PrivateKeyEntry).privateKey
val publicKey: PublicKey? = keyStore.getCertificate(keyAlias).publicKey
return if (privateKey == null || publicKey == null) {
KeyHolderUtils().generateRSAKeyPair(keyAlias)
} else {
val publicKey2 = getPublicKey(
Base64.encodeToString(
publicKey.encoded,
EncryptionConstants.BASE_64_OPTIONS
)
)
KeyPair(publicKey2, privateKey)
}
}catch (ex: Exception){
return KeyHolderUtils().generateRSAKeyPair(keyAlias)
}
}
Encrypt, Sign and Verify works, but when I try to decrypt always got en KeyStoreException: Unknown error
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.io.IOException: javax.crypto.IllegalBlockSizeException
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:133)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:202)
at com.test.communication.encryption.utils.EncryptionUtils.decryptStringRSA(EncryptionUtils.kt:46)
at com.test.communication.encryption.AndroidEncryptionBase.decryptMessage(AndroidEncryptionBase.kt:72)
at com.test.communication.encryption.AndroidEncryption.decryptMessage(AndroidEncryption.kt:37)
at com.test.communication.server.MessageServiceImpl.send(MessageServiceImpl.kt:97)
at com.test.app.presentation.ui.main.CommunicationActivity$initButtons$1.onClick(CommunicationActivity.kt:64)
at android.view.View.performClick(View.java:7201)
at android.view.View.performClickInternal(View.java:7170)
at android.view.View.access$3500(View.java:806)
at android.view.View$PerformClick.run(View.java:27562)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7682)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:531)
at javax.crypto.Cipher.doFinal(Cipher.java:2002)
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:130)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:202)
at com.test.communication.encryption.utils.EncryptionUtils.decryptStringRSA(EncryptionUtils.kt:46)
at com.test.communication.encryption.AndroidEncryptionBase.decryptMessage(AndroidEncryptionBase.kt:72)
at com.test.communication.encryption.AndroidEncryption.decryptMessage(AndroidEncryption.kt:37)
at com.test.communication.server.MessageServiceImpl.send(MessageServiceImpl.kt:97)
at com.test.app.presentation.ui.main.CommunicationActivity$initButtons$1.onClick(CommunicationActivity.kt:64)
at android.view.View.performClick(View.java:7201)
at android.view.View.performClickInternal(View.java:7170)
at android.view.View.access$3500(View.java:806)
at android.view.View$PerformClick.run(View.java:27562)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7682)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: android.security.KeyStoreException: Unknown error
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1303)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:531)
at javax.crypto.Cipher.doFinal(Cipher.java:2002)
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:130)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:202)
at com.test.communication.encryption.utils.EncryptionUtils.decryptStringRSA(EncryptionUtils.kt:46)
at com.test.communication.encryption.AndroidEncryptionBase.decryptMessage(AndroidEncryptionBase.kt:72)
at com.test.communication.encryption.AndroidEncryption.decryptMessage(AndroidEncryption.kt:37)
at com.test.communication.server.MessageServiceImpl.send(MessageServiceImpl.kt:97)
at com.test.app.presentation.ui.main.CommunicationActivity$initButtons$1.onClick(CommunicationActivity.kt:64)
at android.view.View.performClick(View.java:7201)
at android.view.View.performClickInternal(View.java:7170)
at android.view.View.access$3500(View.java:806)
at android.view.View$PerformClick.run(View.java:27562)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7682)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
I try different combinations of bloks(ECB, NONE) and paddings(NOPadding, PKCS1Padding, OAEPPadding, OAEPwithSHA-1andMGF1Padding and OAEPwithSHA-256andMGF1Padding) with changing KeyGenParameterSpec
params setEncryptionPaddings
, setBlockModes
according combination aso I saw posts in SO like Android KeyStoreException Unknown Error
and try to change code like in it, but always got same Exception.
BTW was try on different emulators and real device OnePlus 6 result the same - KeyStoreException: Unknown error
UPDATE!!! I found some weird think if I copy all this code to Activity all works fine, but when I call it from another class then got an error. For Example:
fun fire(){
val str = "test"
val encryptionUtils = EncryptionUtils()
generateRSAKeyPair("testKeys")
val encryptStringRSA = encryptionUtils.encryptStringRSA(str, keyPair.public)
val decryptStringRSA = decryptStringRSA(encryptStringRSA, keyPair.private)
println(decryptStringRSA)
}
this code works fine, but
fun fire(){
val str = "test"
val encryptionUtils = EncryptionUtils()
generateRSAKeyPair("testKeys")
val encryptStringRSA = encryptionUtils.encryptStringRSA(str, keyPair.public)
val decryptStringRSA = encryptionUtils.decryptStringRSA(encryptStringRSA, keyPair.private)
println(decryptStringRSA)
}
this code cause en exception. EncryptionUtils the class with 100% same code as in Activity (I mean encryptStringRSA, decryptStringRSA and other)