2

I was using a REST service written in PHP in my android app without much trouble. Now I'm trying to use it in a Windows Phone app and I'm getting crazy already!

What I know so far: Silverlight will accept only Aes in CBC mode and PKCS7 padding.

What I get: "Padding is invalid and can not be removed" exception at (see full code at the bottom):

plaintext = srDecrypt.ReadToEnd();

If I crypt and decrypt in C#, using the same configs, it works fine. When I try to decript in C# from a PHP crypted string, it fails with the error mentioned above.

My PHP script do the following:

function encrypt128($message) {
    $vector = "DB96A56CCA7A69FC";
    $key = "6DBC44F54CA3CFDEDDCA140CA46A99C1"; // PHP md5 function leaves it in lower case, so I just copied the key from C# debug.

    //PKCS7 Padding
    $block = mcrypt_get_block_size('rijndael_128', 'cbc');
    $pad = $block - (strlen($message) % $block);
    $message.= str_repeat(chr($pad), $pad);

    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
    mcrypt_generic_init($cipher, $key, $vector);
    $result = mcrypt_generic($cipher, $message);
    mcrypt_generic_deinit($cipher);

    return base64_encode($result);
}

And in C# (Silverlight / Windows Phone 7) I use the following to decrypt:

//Where buffer is the string data I got after calling the PHP REST service.
DecryptStringFromBytes(Convert.FromBase64String(buffer), MD5Core.GetHash("7a272d3e41372c547a272d3e41372c54"), System.Text.Encoding.UTF8.GetBytes("DB96A56CCA7A69FC"));

static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
    // Check arguments.
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

    // Declare the string used to hold
    // the decrypted text.
    string plaintext = null;

    // Create an RijndaelManaged object
    // with the specified key and IV.
    using (AesManaged rijAlg = new AesManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for decryption.
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {

                    // Read the decrypted bytes from the decrypting stream
                    // and place them in a string.
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
    }
    return plaintext;
}

The big question is: what am I doing wrong?

Thanks in advance!

WWW
  • 9,734
  • 1
  • 29
  • 33
  • if you encrypt a very small string in each method, and compare the outputs, do they differ? how? – Jonathan Jul 24 '12 at 23:58
  • If I encode "Test", I get "eScuqAGH8L6cKaRG9ii+uw==" in C# and "0RysWwzyHHDnwcf0cIQ8xg==" in PHP. – Marcos Felipe Jul 25 '12 at 12:27
  • I changed the StreamWriter constructor to test all available encoding types (UTF8, Unicode, BigEndian), but C# still generate a different encoded string. – Marcos Felipe Jul 25 '12 at 14:11

2 Answers2

0

So here is the answer:

I droped the MD5 crap out of PHP and C#, and they are now working properly.

Just in case you dropped here looking for the same answer, here is a sample code. Don't forget to make your own key and iv (although those bellow will work, is not recommended to use!)

PHP:

function encrypt128($message) {
    $vector = "0000000000000000";
    $key = "00000000000000000000000000000000";

    $block = mcrypt_get_block_size('rijndael_128', 'cbc');
    $pad = $block - (strlen($message) % $block);
    $message .= str_repeat(chr($pad), $pad);

    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
    mcrypt_generic_init($cipher, $key, $vector);
    $result = mcrypt_generic($cipher, $message);
    mcrypt_generic_deinit($cipher);

    return base64_encode($result);
}

C#:

byte[] cripted = EncryptStringToBytes("Test", System.Text.Encoding.UTF8.GetBytes("00000000000000000000000000000000"), System.Text.Encoding.UTF8.GetBytes("0000000000000000"));
  • Marcos, you are not using your vector at all. Also your vector length should be equal to key size. for cipher name and method better use `MCRYPT_RIJNDAEL_128`, `MCRYPT_MODE_CBC` which are internally defined and will not be interpreted to anything else by accident. `System.Text.Encoding.UTF8.GetBytes` will give you unexpected IV data, you can either use `byte[] Key = new byte[] { 0x00, ....};` or `Encoding.ASCII.GetBytes(...)` which the first method is usually used in finance industry for security key definations. – AaA Feb 27 '15 at 16:31
-1

Encrypt/Decrypt using PHP:

class Cipher {
    private $key, $iv;
    function __construct() {
        $this->key = "edrtjfjfjlldldld";
        $this->iv = "56666852251557009888889955123458";
    }
    function encrypt($text) {

        $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
        $padding = $block - (strlen($text) % $block);
        $text .= str_repeat(chr($padding), $padding);
        $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->key, $text, MCRYPT_MODE_CBC, $this->iv);

        return base64_encode($crypttext);
    }

    function decrypt($input) {
        $dectext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->key, base64_decode($input), MCRYPT_MODE_CBC, $this->iv);
        return $dectext;
    }
}

Encrypt/Decrypt using C#:

public class RijndaelSimple
    {
        const string iv = "56666852251557009888889955123458";
        const string key = "edrtjfjfjlldldld";

        static public String EncryptRJ256(string plainText)
        {
            var encoding = new UTF8Encoding();
            var Key = encoding.GetBytes(key);
            var IV = encoding.GetBytes(iv);
            byte[] encrypted;

            using (var rj = new RijndaelManaged())
            {
                try
                {
                    rj.Padding = PaddingMode.PKCS7;
                    rj.Mode = CipherMode.CBC;
                    rj.KeySize = 256;
                    rj.BlockSize = 256;
                    rj.Key = Key;
                    rj.IV = IV;

                    var ms = new MemoryStream();

                    using (var cs = new CryptoStream(ms, rj.CreateEncryptor(Key, IV), CryptoStreamMode.Write))
                    {
                        using (var sr = new StreamWriter(cs))
                        {
                            sr.Write(plainText);
                        }
                        encrypted = ms.ToArray();
                    }
                }
                finally
                {
                    rj.Clear();
                }
            }

            return Convert.ToBase64String(encrypted);
        }

        static public String DecryptRJ256(string input)
        {
            byte[] cypher = Convert.FromBase64String(input);

            var sRet = "";

            var encoding = new UTF8Encoding();
            var Key = encoding.GetBytes(key);
            var IV = encoding.GetBytes(iv);

            using (var rj = new RijndaelManaged())
            {
                try
                {
                    rj.Padding = PaddingMode.PKCS7;
                    rj.Mode = CipherMode.CBC;
                    rj.KeySize = 256;
                    rj.BlockSize = 256;
                    rj.Key = Key;
                    rj.IV = IV;
                    var ms = new MemoryStream(cypher);

                    using (var cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
                    {
                        using (var sr = new StreamReader(cs))
                        {
                            sRet = sr.ReadLine();
                        }
                    }
                }
                finally
                {
                    rj.Clear();
                }
            }

            return sRet;
        }

    }
Melad
  • 855
  • 12
  • 14
  • 2
    That code doesn't work in Windows Phone. WP does not have the class RijndaelManaged – derp_in_mouth Mar 23 '14 at 00:21
  • i tried out the c# code and this simple test doesn't work out string encryptedString = EncryptRJ256(teststring); string decryptedString = DecryptRJ256(encryptedString); Assert.AreEqual(decryptedString, teststring); – Alex Maker Oct 07 '14 at 13:19
  • @AlexMaker, this works : string teststring = "The quick brown fox jumps over the lazy dog"; string encryptedString = RijndaelSimple.EncryptRJ256(teststring); string decryptedString = RijndaelSimple.DecryptRJ256(encryptedString); bool bAreEqual = (decryptedString == teststring ? true : false); // bAreEqual = true – Melad Oct 10 '14 at 13:48