3

We have the following C# and PHP code, but the results are not matching. We need the coded string from C# to be decoded using PHP script. Is this possible?

C# code

class Program
{

  public string CKey = "HELLOSECRETTEXT";
  public string EnText = "Hello World";

  static void Main(string[] args)
    {
        Functions fn = new Functions();
        Program pr = new Program();
        Console.Write(fn.Encrypt(EnText, pr.CBDKey));
    }

}


class Functions
{

    //Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
    public String Encrypt(String plainText, String key)
    {
        var plainBytes = Encoding.UTF8.GetBytes(plainText);
        return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key)));
    }

    //Decrypts encrypted string and returns a base64 encoded string
    public String Decrypt(String encryptedText, String key)
    {
        var encryptedBytes = Convert.FromBase64String(encryptedText);
        return Encoding.UTF8.GetString(Decrypt(encryptedBytes, GetRijndaelManaged(key)));
    }

    private RijndaelManaged GetRijndaelManaged(String secretKey)
    {
        var keyBytes = new byte[16];
        var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
        Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
        return new RijndaelManaged
        {
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7,
            KeySize = 128,
            BlockSize = 128,
            Key = keyBytes,
            IV = keyBytes
        };
    }

    public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
    {
        return rijndaelManaged.CreateEncryptor()
            .TransformFinalBlock(plainBytes, 0, plainBytes.Length);
    }

    public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged)
    {
        return rijndaelManaged.CreateDecryptor()
            .TransformFinalBlock(encryptedData, 0, encryptedData.Length);
    }

}

Result: DeM0gfGHLzuSLtx4siKdVg==

And this decrypts to the text "Hello World".

PHP code

<?php
//Encryption function

function encryptAPIData($data) {

    $key = "HELLOWORLDHELLO"; //Key with 15 characters

    //Serialize data before encryption
    $encrypt_data = serialize($data);

    //Find out what size is supported for IV
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);

    #Create a random IV to use with CBC encoding
    $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

    //Create a cipher text compatible with AES (Rijndael block size = 128) with CBC Mode
    $encrypted_data = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $encrypt_data, MCRYPT_MODE_CBC, $iv);

    //Encode data to send it and attach IV with it for decryption - pipe delimited
    $encoded = base64_encode($encrypted_data) . '|' . base64_encode($iv);

    return $encoded;
  }


//Decryption function 

function decryptAPIData($data) {   

    $key = "HELLOWORLDHELLO"; //Key with 15 characters

    $decrypt_data = explode('|', $data . '|');
    $decoded = base64_decode($decrypt_data[0]);    
    $iv = base64_decode($decrypt_data[1]);

    if (strlen($iv) !== mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)) {
      return false;
    }

    $decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv));
    $decrypted = unserialize($decrypted);
    return $decrypted;
  }


    $text_to_encrypt = "Hello World";
    echo "<br><br>";
    echo "Original text: \t\t\t{$text_to_encrypt}\n";
    echo "<br><br>";
    $encrypted_data = encryptAPIData($text_to_encrypt);
    echo "Text after encryption: \t\t" . $encrypted_data . "\n";
    echo "<br><br>";
    echo "Text after decryption: \t\t" . decryptAPIData($encrypted_data) . "\n";
    echo "<br><br>";


?>

Result:

Original text: Hello World

Text after encryption:

iKIlK70iSWtSMgT0ld/UuhT9/Aa5dZBEUKDQ57jiF9w=|L8oIcim4jphVim8SYdJRwQ== 

Text after decryption: "Hello World"


Now when trying to decrypt the text output by C# using above PHP code, it returns the following error/warning.

Notice: unserialize(): Error at offset 0 of 16 bytes

Any ideas on why this error happens?

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Vinod
  • 31
  • 2
  • 2
    It is best not to use PHP mcrypt, it is abandonware, has not been updated in years and does not support standard PKCS#7 (née PKCS#5) padding, only **non-standard null padding** that can't even be used with binary data. mcrypt has many outstanding [bugs](https://sourceforge.net/p/mcrypt/bugs/) dating back to 2003. The mcrypt-extension is deprecated will be removed in PHP 7.2. Instead consider using [defuse](https://github.com/defuse/php-encryption) or [RNCryptor](https://github.com/RNCryptor), they provide a complete solution and are being maintained and is correct. – zaph Nov 07 '17 at 13:28

1 Answers1

0

You are trying to unserialize data that hasn't been serialized. Encoding / decoding and serialization / deserialization aren't the same thing. Encoding means the (binary) representation of data in a described way; encoding generally is system independent. However serialization is the storage of system specific objects into a binary representation; it usually contains type identifiers and such. So you need to get rid of that functionality to interoperate.

Besides that you do indeed need to implement PKCS#7 compliant padding / unpadding for PHP. The PHP pages of mcrypt show ways of doing that in the comments of the API documentation, but you'd better remove mcrypt altogether as zaph already mentioned in his comment over here.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263