-3

I am working on a data exchange integration with my client and the data they send me is encrypted using their C# encrypt method (below).

My app is running PHP 5.3 and I need an equivalent code to decrypt the data they send. I have the PHP code but it'd not decrypt the client data correctly for me.

Clearly I am making some mistake in my encryption/decryption methods, IV key or something. Can anyone spot the mistake?

Thanks.

C# Code (From my client):

using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

public class Program
{
    public static void Main()
    {
        var text = "this is a plain string";
        var enc = Program.Encrypt(text);
        Console.WriteLine(enc);
        Console.WriteLine(Program.Decrypt(enc));
    }

    public static string Encrypt(string clearText)
    {
        var EncryptionKey = "1234567890123456";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            byte[] IV = new byte[15];
            var rand = new Random();
            rand.NextBytes(IV);
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }

                clearText = Convert.ToBase64String(IV) + Convert.ToBase64String(ms.ToArray());
            }
        }

        return clearText;
    }

    public static string Decrypt(string cipherText)
    {
        var EncryptionKey = "1234567890123456";
        byte[] IV = Convert.FromBase64String(cipherText.Substring(0, 20));
        cipherText = cipherText.Substring(20).Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }

                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }

        return cipherText;
    }
}

PHP Code I have:

public function encrypt($plainText)
{
    $secretKey = '1234567890123456';

    return rtrim(
        base64_encode(
            mcrypt_encrypt(
                MCRYPT_RIJNDAEL_256,
                $secretKey, $plainText,
                MCRYPT_MODE_ECB,
                mcrypt_create_iv(
                    mcrypt_get_iv_size(
                        MCRYPT_RIJNDAEL_256,
                        MCRYPT_MODE_ECB
                    ),
                    MCRYPT_RAND)
            )
        ), "\0"
    );
}

public function decrypt($encodedData)
{
    $secretKey = '1234567890123456';

    return rtrim(
        mcrypt_decrypt(
            MCRYPT_RIJNDAEL_256,
            $secretKey,
            base64_decode($encodedData),
            MCRYPT_MODE_ECB,
            mcrypt_create_iv(
                mcrypt_get_iv_size(
                    MCRYPT_RIJNDAEL_256,
                    MCRYPT_MODE_ECB
                ),
                MCRYPT_RAND
            )
        ), "\0"
    );
}
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206

2 Answers2

10

Can anyone spot the mistake?

Yes, and the big one isn't really your fault: mcrypt's confusing API strikes again.

That said, there are actually multiple mistakes here.

return rtrim( // unnecessary
    base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_256, // Not AES
            $secretKey, $plainText,
            MCRYPT_MODE_ECB, // BAD, use MCRYPT_MODE_CBC or 'ctr' instead
            mcrypt_create_iv(
                mcrypt_get_iv_size(      // unless you're going make this
                    MCRYPT_RIJNDAEL_256, // configurable, you should just
                    MCRYPT_MODE_ECB      // hard-code this as an integer
                ),
                MCRYPT_RAND) // BAD, use MCRYPT_DEV_URANDOM
        )
    ), "\0"
); 

If you're going to generate an IV, it should be communicated so your recipient can decrypt the same first block successfully. The C# code does this, the PHP does not.

From a cryptography engineering perspective, you should consider, both in C# land and in PHP, deploying an Encrypt then Authenticate protocol. See this blog post on encryption and authentication. Also, all the crypto code you've ever written is probably broken.

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
0

It seems like the PHP Script is Using the wrong Mode:
https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm.mode%28v=vs.110%29.aspx
The C# Functions do not set any Mode so the default is CBC.
The PHP part uses ECB instead, which is not only wrong, but insecure.

EaranMaleasi
  • 895
  • 1
  • 13
  • 32
  • 1
    That's not the only thing. OP uses Rijndael-128 in C# and Rijndael-256 in PHP. OP doesn't derive key and IV in PHP. OP also uses random IV in both encryption *and decryption* in the PHP code. (Feel free to add this to your answer.) – Artjom B. Oct 01 '15 at 10:46
  • Just edited the post to make it more specific. Also made a few corrections in the code suggested by Artjom B and EaranMaleasi (thank a lot). The code still does not work. Any more suggestions? I'll keep on looking on my own meanwhile. – Ather Hashmi Oct 01 '15 at 11:36
  • @AtherHashmi I rolled back your edit. Please don't change your question after receiving an answer. Your change invalidated the answer. – Artjom B. Oct 01 '15 at 12:21