Search code examples
androidencryptionrsaandroid-keystore

Android Keystore multipurpose key generation


I'm trying to generate a RSA key pair for this pruposes:

val purposes = PURPOSE_DECRYPT or PURPOSE_ENCRYPT or PURPOSE_SIGN or PURPOSE_VERIFY

And this is my key generation code:

val generator = KeyPairGenerator.getInstance(
                KEY_ALGORITHM,
                ANDROID_KEY_STORE
            )
generator?.initialize(
                    KeyGenParameterSpec.Builder(
                        alias,
                        purposes
                    )
                        .setDigests(KeyProperties.DIGEST_SHA256)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
                        .build()
                )
generator?.generateKeyPair()

However when I use this purposes the decrypt does not work and throws an exception:

InvalidKeyException: "keystore operation  failed. Incompatible purpose."

But when I try just encryption and decryption, keystore does encrypt and also decrypt perfectly. this is the purposes that I use for:

val purposes = PURPOSE_DECRYPT or PURPOSE_ENCRYPT

These are the methods for encryption and decryption:

private fun decrypt(cipherText: String, alias: String): String? {
        return try {
            val privateKeyEntry = getGeneratedPrivateKey(alias)

            val output = Cipher.getInstance(
                "$KEY_ALGORITHM_RSA/$BLOCK_MODE_ECB/$ENCRYPTION_PADDING_RSA_PKCS1"
//                ANDROID_OPEN_SSL
            )
            output.init(Cipher.DECRYPT_MODE, privateKeyEntry?.privateKey)

            val inputStream = ByteArrayInputStream(
                android.util.Base64.decode(
                    cipherText,
                    android.util.Base64.NO_WRAP
                )
            )

            val res = String(CipherInputStream(inputStream, output).readBytes(), Charsets.UTF_8)
            res
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

private fun encrypt(plainText: String, alias: String): String? {
        return try {
            val publicKey = getGeneratedPublicKey(alias) ?: setupKeyPair(
                alias,
                PURPOSE_ENCRYPT or PURPOSE_DECRYPT
            )?.public
            val cipher = Cipher.getInstance(
                "$KEY_ALGORITHM_RSA/$BLOCK_MODE_ECB/$ENCRYPTION_PADDING_RSA_PKCS1"
//                ANDROID_OPEN_SSL
            )
            cipher.init(Cipher.ENCRYPT_MODE, publicKey)

            val outputStream = ByteArrayOutputStream()
            val cipherOutputStream = CipherOutputStream(outputStream, cipher)
            cipherOutputStream.write(plainText.toByteArray(charset("UTF-8")))
            cipherOutputStream.close()

            val encryptedText = outputStream.toByteArray()
            outputStream.close()
            val res = android.util.Base64.encodeToString(encryptedText, android.util.Base64.NO_WRAP)
            res
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

So what is the problem? How can I make a multipurpose keypair in Android keystore?


Solution

  • Probably you have your own custom variables for purpose keys

    You should use KeyProperties in this line:

    val purposes = PURPOSE_DECRYPT or PURPOSE_ENCRYPT or PURPOSE_SIGN or PURPOSE_VERIFY
    

    like this:

    val purposes = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY