0

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)

Volodymyr
  • 51
  • 6
  • Please complete your code to a [mcve](https://stackoverflow.com/help/minimal-reproducible-example). How is `getPublicKey` implemented, what are the definitions of `EncryptionConstants.BASE_64_OPTIONS` and `ANDROID_KEYSTORE`? Even if some of them can be guessed, there is a risk of working with different assumptions. In particular, post the complete calls of the methods for encryption and decryption, which finally raise the exception, _so that a repro is possible_. – Topaco Aug 28 '20 at 16:09
  • Sorry, forgot to say that val publicKey2 = getPublicKey( Base64.encodeToString( publicKey.encoded, EncryptionConstants.BASE_64_OPTIONS ) ) without this code have the same behavior – Volodymyr Aug 29 '20 at 10:00
  • ANDROID_KEYSTORE = "AndroidKeyStore" – Volodymyr Aug 29 '20 at 10:03
  • and one more what I found, if I copy all this code to direct to activity, all works fine, but if I call them from another class then got error. – Volodymyr Aug 29 '20 at 14:36

1 Answers1

0

I found temp solution. I just rename EncryptionUtils -> EncryptionHelper and now it works, but after some time error came's back and each time renaming is not a solution! Hopes someone helps with this

Volodymyr
  • 51
  • 6