PHP Asymmetric Encryption With Android Payload
In this post I will show you how I implemented asymmetric encryption with Android and a host PHP server.

In this post I will show you the method I used to implement PHP asymmetric encryption with Android to secure payload communication.

I will not dive into how to send the data. The focus here is on how to perform asymmetric encryption in Android and PHP. We will using a public key in Android to encrypt the data. Then we will send the encrypted data to a hosted PHP server to decrypt using a private key.

This is just proof of concepts to help people understand how to implement. Please add best practice and security measures to your own design.

Android Asymmetric Encryption With PHP

Code

Setup

In Android, the public key is saved in the project directory in the res/raw folder. NOTE: Not best practice, this is just to implement the core logic and post proof of concept.

The Android code is based on one of the answers in this Stackoverflow question. This was extremely useful.

https://stackoverflow.com/questions/43532954/android-encrypting-string-using-rsa-public-key

SecureMessage

I created a class file named SecureMessage.

object SecureMessage {

    fun getTheKey(pk : String) : String {
        val key = pk.replace("\\r".toRegex(), "")
            .replace("\\n".toRegex(), "")
            .replace(System.lineSeparator().toRegex(), "")
            .replace("-----BEGIN PUBLIC KEY-----", "")
            .replace("-----END PUBLIC KEY-----", "")
            .trim()
        Log.d("Public Key", key)
        return key
    }

    fun encryptData(txt: String, publicKey: String): String? {
        val pk = getTheKey(publicKey)
        var encoded = ""
        var encrypted: ByteArray? = null
        try {
            val publicBytes: ByteArray = Base64.decode(pk, Base64.DEFAULT)
            val keySpec = X509EncodedKeySpec(publicBytes)
            val keyFactory: KeyFactory = KeyFactory.getInstance("RSA")
            val pubKey: PublicKey = keyFactory.generatePublic(keySpec)
            val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING")
            cipher.init(Cipher.ENCRYPT_MODE, pubKey)
            encrypted = cipher.doFinal(txt.toByteArray())
            encoded = Base64.encodeToString(encrypted, Base64.DEFAULT)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return encoded
    }
}

LaunchEffect To Perform Encryption

Now to call this, we implement a method as shown below. I am using Compose and calling it through a LaunchEffect on startup. NOTE: Not best practice, this is just to ensure I can encrypt and decrypt data.

LaunchedEffect(Unit) {

        val inputStream : InputStream = applicationContext.resources.openRawResource(R.raw.pk)
        val bytes : ByteArray = inputStream.readBytes()
        val pk = String(bytes)
        Log.d("Public Key", pk)
        val encryptedData = SecureMessage.encryptData("Hello World!", pk)
        if (encryptedData != null) {
            Log.d("Encrypted Data", encryptedData)
            //take the encryptedData string and paste it in the $data variable on the PHP server. This validates proof of concept.
        }
        
}

PHP

The PHP code to place on the hosted server. You can place this where you need it. I am just testing so I created a separate test file to retrieve the data.

<?php

//.....

$data = "<encrypted data from Android Logcat>";

$test_pk = "-----BEGIN PRIVATE KEY-----
.....
PRIVATE KEY HERE - AGAIN, JUST FOR TESTING PURPOSES
.....
-----END PRIVATE KEY-----";

openssl_private_decrypt(base64_decode($data), $test_decrypted, $test_pk);

echo "This is the decrypted text:<br />";
var_dump($test_decrypted);
//........

?>

Hope this helps people move in the right direction with their encryption project. Use this information to create forward momentum with your project. NOTE: I am not an encryption expert so please do your research.

BONUS

I messed around with different methods of receiving the data and how to decrypt it on the PHP side. The following methods shown below decrypt the data payload on the PHP server with the private key.

Method #1

If you just are testing both keys in PHP, use this sample. This will:

  • create the public key and private keys.
  • encrypt the $test JSON array with the public key.
  • decrypt with the private key.
  • then finally display the $test JSON back again.
$test = json_encode(array('id' => 'Jack', 'username' => 'JackHollow'));

// Configuration settings for the key
$config = array(
    "digest_alg" => "sha1",
    "private_key_bits" => 512,
    "private_key_type" => OPENSSL_KEYTYPE_RSA,
);

// Create the private and public key
$res = openssl_pkey_new($config);

// Extract the private key into $private_key
openssl_pkey_export($res, $private_key);

// Extract the public key into $public_key
$public_key = openssl_pkey_get_details($res);
$public_key = $public_key["key"];

echo "Public Key:<br />".$public_key;
echo "<br /><br /><br />Private Key:<br />".$private_key;

// -------------------------Encrypt using the public key
openssl_public_encrypt($test, $encrypted, $public_key);

$encrypted_hex = bin2hex($encrypted);
echo "<br /><br /><br />";
echo "This is the encrypted text: $encrypted_hex\n\n";

// -------------------------Decrypt the data using the private key
openssl_private_decrypt($encrypted, $decrypted, $private_key);

echo "#1 Method - This is the decrypted text: $decrypted\n\n";

Method #2

This is the method I showed above in the main post. The data is encrypted using the public key in Android. Data payload is sent to the logcat. Copy/Paste from logcat to this PHP script as the $data variable. The PHP script then decrypts it using the private key.

$data = ""; //<--- message created in Android - ex. {"message":"Decrypted Message From Android!"}


$test_pk = "-----BEGIN PRIVATE KEY-----
....
<generated private key that matches the public key, can use script #1 to create both to sample>
....
-----END PRIVATE KEY-----";

openssl_private_decrypt(base64_decode($data), $test_decrypted, $test_pk);

echo "<br /><br /><br />";
echo "#2 - This is the decrypted text:$test_decrypted<br />";
echo "<br /><br /><br />";

//---------------#2 Works

Method #3

Learning more about the process helped create this next method I want to share due to an issue I had. The script just takes the string $tryit and decodes it to JSON so I can access each by their key.

The issue I had here was the string wasn't able to go straight to json_decode. I needed to use the mb_convert_encoding to put the string as UTF-8 for the json_encode function. Took a while to figure out. Useful if anyone needs it.

$tryit = "{\"message\":\"Happy Coding\"}";

$tryit = mb_convert_encoding($tryit, "UTF-8");

$json = json_decode($tryit, true);

echo "#3 - This is the decrypted text:{$json['message']}<br />";
var_dump($json);

Method #4

Last method here gives the idea of how to receive the data. For testing I am using the $_GET[''] method so I can alter and test data as much as needed to validate functionality.

Here I took the encrypted data message from the Android logcat ( similar to method #2 ) and pasted it in the url as:

<serverAddress>/index.php?data=<encryptedData>

Alter the above with your server address and where the below PHP script will reside, i.e. index.php. The GET parameter starts with ?data=

$data = $_GET['data'];

$test_pk = "-----BEGIN PRIVATE KEY-----
....
<generated private key that matches the public key, can use script #1 to create both to sample>
....
-----END PRIVATE KEY-----";

openssl_private_decrypt($data, $test_decrypted, $test_pk);

$test_decrypted = mb_convert_encoding($test_decrypted, "UTF-8");

$json = json_decode($test_decrypted, true);

echo "<br /><br /><br />";
echo "$4 - This is the decrypted text:{$json['message']}<br />";
echo "<br /><br /><br />";

Method #5

Finally, if you needed to get the data packet from an app being sent through POST, this is an example. By the way, the app is on Android using Retrofit for communication.

//the data in this example is sent in this format - {"message":<encrypted data string>}

$data = json_decode(file_get_contents('php://input'));

$test_pk = "-----BEGIN PRIVATE KEY-----
....
<generated private key that matches the public key, can use script #1 to create both to sample>
....
-----END PRIVATE KEY-----";

$data_str= $data->{'message'};

$data_str= mb_convert_encoding($data_str, "UTF-8");

openssl_private_decrypt(base64_decode($data_str), $test_decrypted, $test_pk);

$json = json_decode($test_decrypted, true);

Hope this was useful.