2

This is my very first attempt at cryptography and I am having trouble with porting the encryption from PHP to C#.

I had searched the internet for a working solution to my problem but everything I have tried does not work. I am getting different results between the two languages.

In PHP I have the following code:

function encrypt($Key, $strToEncrypt){
    $md5Key = md5(pack("H*", $Key));
    $md5Iv = md5($Key);

    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);

    $padding = $block - (strlen($strToEncrypt) % $block);
    $strToEncrypt .= str_repeat(chr($padding), $padding);

    $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $md5Key, $strToEncrypt, MCRYPT_MODE_CBC, $md5Iv);
    $enc2 = base64_encode($enc);

    return $enc2;
}

and in C# the following code:

    public string Encrypt(string strToEncrypt)
    {
        string ret;
        var pKey = PackH(_appkey);
        var md5Key = CalcMd5(pKey);
        var iv = CalcMd5(_appkey);

        var enc =Encoding.UTF8;
        var eIv = enc.GetBytes(iv);
        var eKey = enc.GetBytes(md5Key);

        using (var rij = new RijndaelManaged { BlockSize = 256, KeySize = 256, IV = eIv, Key = eKey, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros})
        using (var memoryStream = new MemoryStream())
        using (var cryptoStream = new CryptoStream(memoryStream, rij.CreateEncryptor(eKey, eIv), CryptoStreamMode.Write))
        {
            using (var sw = new StreamWriter(cryptoStream))
            {
                sw.Write(strToEncrypt);

            }
            ret = Convert.ToBase64String(memoryStream.ToArray());
        }


        return ret;
    }

The C# Pack function:

protected byte[] PackH(string hex)
    {
        if ((hex.Length % 2) == 1) hex += '0';
        var bytes = new byte[hex.Length / 2];
        for (var i = 0; i < hex.Length; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        }
        return bytes;
    }

And the C# CalcMd5 function:

protected string CalcMd5(string textToEnc)
    {
        var sB = new StringBuilder();
        using (var mdHash = MD5.Create())
        {
            var cHash = mdHash.ComputeHash(Encoding.UTF8.GetBytes(textToEnc));

            foreach (byte t in cHash)
            {
                sB.Append(t.ToString("x2"));
            }
        }

        return sB.ToString();
    }

I have another CalcMd5 function that takes in a byte[] (it is like the one above but does not have the GetBytes part).

The keys and the string that needs encrypting are the same both in PHP and C#:

The Key: "24acd2fcc7b20b8bd33ff45176f03061a09b729487e10d2dd38ab917" and

The string that I want to encode: "110114135AB96637711100"

In C# the result of the function is:"LHTqpxCJrONmbDdUFHyUZZUVf94z1RmSXWo85/wyEew=" while in PHP is: "5MkCjfs0vp2HSKdY5XPUAuV68YsrP31Q+ddZsd5p7Sc=".

I have tried modifying the padding mode in C#, also tried different methods found on the stackoverflow site but none of them works.

I have checked and the final key and Iv that are passed to the mcrypt function and RijndaelManaged function are the same and both have 32 byte size.

The oddly part is that the decryption functions are working very well (it is working to decrypt the PHP encrypted string with C# function and the other war around C# encrypted string is decrypted with the PHP function).

Could it be a problem with the encoding? Or maybe the padding? Or is there something else that I have overlooked?

  • By the way, I always cringe when I see MD5 used in a security context. It's not necessarily always a problem, but as a hash function it is considered quite insecure by now. I believe we're at SHA-3 now (though there are some controversies even there). – Mikkel K. Aug 22 '14 at 10:08
  • Oh, I also noticed your IV seems to be a function of the key. It's supposed to be random. – Mikkel K. Aug 22 '14 at 10:12

1 Answers1

2

The problem seems to be your padding, on PHP-side you are manually doing PKCS7-Padding:

$padding = $block - (strlen($strToEncrypt) % $block);
$strToEncrypt .= str_repeat(chr($padding), $padding);

And on C#-side you are using:

Padding = PaddingMode.Zeros

To fix this you could either modify the PHP-code by removing the above mentioned two lines since mcrypt() does automatically do ZeroBytePadding for you.

Or you could change the padding in C# to:

Padding = PaddingMode.PKCS7
i_turo
  • 2,679
  • 1
  • 13
  • 15
  • 1
    I recommend using PKCS7 on both sides. Zero padding cannot be removed unless you make assumptions about the plaintext (that it doesn't end with a zero byte). Good encryption works on any sequence of bytes. – CodesInChaos Aug 27 '14 at 10:30