'PHP and Android Keystore encryption / decryption

I've been trying to get this for hours now, and I can't find what's wrong. I'm using a php RESTful API that I made to encrypt data using asymmetric encryption.

First, I save my user's public key in the server by exporting it in android:

fun exportPublicKey() : String {
        val publicKey = getPublicKey()
        return android.util.Base64.encodeToString(
            publicKey!!.encoded,
            android.util.Base64.NO_WRAP
        )
    }

This allows me in the PHP server to do that:

$public_key_core = $_POST["public_key"];
$public_key = "-----BEGIN PUBLIC KEY-----\n" . $public_key_core . "\n-----END PUBLIC KEY-----";

I am unsure that's the right way but openssl seems to be "ok" with that key ?

I then tested my keystore in local using both keys, and it works just fine using this code:

Encrypt:

fun encryptAsymmetricData(data: String, usePrivateKey : Boolean = true): ByteArray {
        val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
        val encryptedBytes: ByteArray
        if (usePrivateKey){
            cipher.init(Cipher.ENCRYPT_MODE, getPrivateKey())
            encryptedBytes = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey())
            encryptedBytes= cipher.doFinal(data.toByteArray(Charsets.UTF_8))
        }
        return encryptedBytes
    }

Decrypt:

fun decryptAsymmetricData(data: ByteArray): String{
        val cipher : Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")

        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey())
        return cipher.doFinal(data).toString(Charsets.UTF_8)
    }

Using this works because I do ".toByteArray(Charsets.UTF_8)" on the encryptData result. Now here's the problem, I use base64 encoding and do the following to encrypt in PHP:

openssl_public_encrypt($token->token, $encrypted_token, $user->public_key);
openssl_public_encrypt($user->id, $encrypted_id, $user->public_key);
[...]
'encrypted_user_id' => base64_encode($encrypted_id),
'encrypted_token' => base64_encode($encrypted_token)

But when I try to decrypt this in Android I'm getting an exception "javax.crypto.IllegalBlockSizeException" caused by this code:

val tokenBA = String(getDecoder().decode(this.encryptedToken), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val userIDBA = String(getDecoder().decode(this.encryptedUserId), Charsets.UTF_8).toByteArray(Charsets.UTF_8)
val token = App.encryptionController.decryptAsymmetricData(tokenBA)
val userID = App.encryptionController.decryptAsymmetricData(userIDBA)

(The logic being, I use base64 to send back my data in PHP, so I convert it to UTF8 in Android, then get the associated ByteArray to decrypt it ?)

I know that the encryption works in "local" but it doesn't when using both PHP and KeyStore, so I guess the problem is coming either from the PHP encryption, or from the way I try to decrypt it in android, but I can't seem to find what wrong, could you guys help me there please ?

Thank you by advance!



Solution 1:[1]

Ok, after searching and making sure the issue wasn't the public key stored in the PHP server, I found the answer. It was caused by the way to convert the "base64" string in an actual ByteArray in the App. This worked:

val token = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedToken))
val userID = App.encryptionController.decryptAsymmetricData(getDecoder().decode(encryptedUserId))

This is only working because I do the "base64_encode" in the server, for some (bad) reason I thought it was needed to go back to UTF8 to get the ByteArray in the app.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 yodamousta