Decryption using Rijndael in C#

keitn picture keitn · Jul 5, 2013 · Viewed 12.3k times · Source

I have the following encryption method. I am not able to decrypt it. I have inherited the encryption algorithm so it cannot be changed.

public static string Encrypt(string plaintext)
    {
        byte[] rgbIV;
        byte[] key;

        RijndaelManaged rijndael = BuildRigndaelCommon(out rgbIV, out key);

        //convert plaintext into a byte array
        byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);

        int BlockSize;
        BlockSize = 16 * (1 + (plaintext.Length / 16));
        Array.Resize(ref plaintextBytes, BlockSize);

        // fill the remaining space with 0
        for (int i = plaintext.Length; i < BlockSize; i++)
        {
            plaintextBytes[i] = 0;
        }

        byte[] cipherTextBytes = null;
        //create uninitialized Rijndael encryption obj
        using (RijndaelManaged symmetricKey = new RijndaelManaged())
        {
            //Call SymmetricAlgorithm.CreateEncryptor to create the Encryptor obj
            var transform = rijndael.CreateEncryptor();

            //Chaining mode
            symmetricKey.Mode = CipherMode.CFB;

            //create encryptor from the key and the IV value
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(key, rgbIV);

            //define memory stream to hold encrypted data
            using (MemoryStream ms = new MemoryStream())
            {
                //define cryptographic stream - contains the transformation key to be used and the mode
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    //encrypt contents of cryptostream
                    cs.Write(plaintextBytes, 0, BlockSize);
                    cs.FlushFinalBlock();

                    //convert encrypted data from a memory stream into a byte array
                    cipherTextBytes = ms.ToArray();
                }
            }
        }

        //store result as a hex value
        string hexOutput = BitConverter.ToString(cipherTextBytes).Replace("-", "");
        hexOutput = hexOutput.Substring(0, plaintext.Length * 2);

        //finially return encrypted string
        return hexOutput;
    }

As you can see it's pretty standard except at the end it's converted to hex and substring is performed. I'm having great difficulty doing the opposite.

My decrypt method is like:

     public static string Decrypt(string disguisedtext)
    {
        byte[] rgbIV;
        byte[] key;

        BuildRigndaelCommon(out rgbIV, out key);

        byte[] disguishedtextBytes = FromHexString(disguisedtext);

        string visiabletext = "";
        //create uninitialized Rijndael encryption obj
        using (var symmetricKey = new RijndaelManaged())
        {
            //Call SymmetricAlgorithm.CreateEncryptor to create the Encryptor obj
            symmetricKey.Mode = CipherMode.CFB;
            //create encryptor from the key and the IV value

            // ICryptoTransform encryptor = symmetricKey.CreateEncryptor(key, rgbIV);
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(key, rgbIV);

            //define memory stream to hold encrypted data
            using (MemoryStream ms = new MemoryStream(disguishedtextBytes))
            {
                //define cryptographic stream - contains the transformation to be used and the mode
                using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {

                    byte[] plaintextBytes = new Byte[disguishedtextBytes.Length];
                    cs.Write(disguishedtextBytes, 0, disguishedtextBytes.Length);
                    cs.FlushFinalBlock();

                    //convert decrypted data from a memory stream into a byte array
                    byte[] visiabletextBytes = ms.ToArray();

                    visiabletext = Encoding.UTF8.GetString(visiabletextBytes);
                }
            }
        }
        return visiabletext;
    }

Helper Methods:

   private static RijndaelManaged BuildRigndaelCommon(out byte[] rgbIV, out byte[] key)
    {
        rgbIV = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x5, 0x6, 0x7, 0x8, 0xA, 0xB, 0xC, 0xD, 0xF, 0x10, 0x11, 0x12 };

        key = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x5, 0x6, 0x7, 0x8, 0xA, 0xB, 0xC, 0xD, 0xF, 0x10, 0x11, 0x12 };

        //Specify the algorithms key & IV
        RijndaelManaged rijndael = new RijndaelManaged{BlockSize = 128, IV = rgbIV, KeySize = 128, Key = key, Padding = PaddingMode.None};           

        return rijndael;
    }

    public static byte[] FromHexString(string hexString)
    {
        if (hexString == null)
        {
            return new byte[0];
        }

        var numberChars = hexString.Length;
        var bytes = new byte[numberChars / 2];

        for (var i = 0; i < numberChars; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
        }

        return bytes;
    }

I'm getting various errors regarding the length of the string and that the padding is invalid. Has anybody any ideas to get the decryption working. I've tried padding out the input string back to 32 bytes but no avail.

Answer

Wolfwyrd picture Wolfwyrd · Jul 5, 2013

Your problem is a subtle error in your Encrypt method. You are losing data from your returned ciphertext by messing with the the hexOutput string. Instead of:

//store result as a hex value
string hexOutput = BitConverter.ToString(cipherTextBytes).Replace("-", "");
hexOutput = hexOutput.Substring(0, plaintext.Length * 2);

//finially return encrypted string
return hexOutput;

You should just return the output:

return BitConverter.ToString(cipherTextBytes).Replace("-", "");

You will also need to change the padding mode in your Decrypt method to None. Though this will now correctly decrypt it will also include the manual padding characters that you add in your encrypt method. As you don't know your plain text you have no GOOD way of removing them. You could always add a method to remove all bytes in your array that dont match your padding value of zero:

int endMarker = decryptedData.Length;
do {    endMarker--; } while (decryptedData[endMarker] == 0);               
Array.Resize(ref decryptedData, endMarker + 1);

However this isn't really a good idea as you're possibly discarding otherwise valid data. A better solution would be to update your encrypt and decrypt methods to let the cipher handle the padding. Putting it all together we get (showing only what i've changed):

private static RijndaelManaged BuildRigndaelCommon(out byte[] rgbIV, out byte[] key)
{
    rgbIV = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x5, 0x6, 0x7, 0x8, 0xA, 0xB, 0xC, 0xD, 0xF, 0x10, 0x11, 0x12 };   
    key = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x5, 0x6, 0x7, 0x8, 0xA, 0xB, 0xC, 0xD, 0xF, 0x10, 0x11, 0x12 };

    //Specify the algorithms key & IV
    RijndaelManaged rijndael = new RijndaelManaged{BlockSize = 128, IV = rgbIV, KeySize = 128, Key = key, Padding = PaddingMode.PKCS7 };    
    return rijndael;
}

public static string Encrypt(string plaintext)
{
    byte[] rgbIV;
    byte[] key;

    RijndaelManaged rijndael = BuildRigndaelCommon(out rgbIV, out key);

    //convert plaintext into a byte array
    byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);

    byte[] cipherTextBytes = null;

    //create uninitialized Rijndael encryption obj
    using (RijndaelManaged symmetricKey = new RijndaelManaged())
    {
        //Call SymmetricAlgorithm.CreateEncryptor to create the Encryptor obj
        var transform = rijndael.CreateEncryptor();

        //Chaining mode
        symmetricKey.Mode = CipherMode.CFB;     
        //create encryptor from the key and the IV value
        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(key, rgbIV);

        //define memory stream to hold encrypted data
        using (MemoryStream ms = new MemoryStream())
        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            //encrypt contents of cryptostream
            cs.Write(plaintextBytes, 0, plaintextBytes.Length);
            cs.Flush();
            cs.FlushFinalBlock();

            //convert encrypted data from a memory stream into a byte array
            ms.Position = 0;
            cipherTextBytes = ms.ToArray();

            ms.Close();
            cs.Close();
        }
    }

    //store result as a hex value
    return BitConverter.ToString(cipherTextBytes).Replace("-", "");
}

public static string Decrypt(string disguisedtext)
{
    byte[] disguishedtextBytes = FromHexString(disguisedtext);

   byte[] rgbIV;
   byte[] key;

   BuildRigndaelCommon(out rgbIV, out key);



   string visiabletext = "";
   //create uninitialized Rijndael encryption obj
   using (var symmetricKey = new RijndaelManaged())
   {
       //Call SymmetricAlgorithm.CreateEncryptor to create the Encryptor obj
       symmetricKey.Mode = CipherMode.CFB;
       symmetricKey.BlockSize = 128;

       //create encryptor from the key and the IV value

       // ICryptoTransform encryptor = symmetricKey.CreateEncryptor(key, rgbIV);
       ICryptoTransform decryptor = symmetricKey.CreateDecryptor(key, rgbIV);

       //define memory stream to hold encrypted data
       using (MemoryStream ms = new MemoryStream(disguishedtextBytes))
       {
           //define cryptographic stream - contains the transformation to be used and the mode
           using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
           {
                byte[] decryptedData = new byte[disguishedtextBytes.Length];
                int stringSize = cs.Read(decryptedData, 0, disguishedtextBytes.Length);
                cs.Close();

                    //Trim the excess empty elements from the array and convert back to a string
                byte[] trimmedData = new byte[stringSize];
                Array.Copy(decryptedData, trimmedData, stringSize);             
                visiabletext = Encoding.UTF8.GetString(trimmedData);
           }
       }
   }
   return visiabletext;
}

Hope this helps point you on your way. As an aside I maintain a set of encryption utilities on Snipt that may be of use to you, particularly the SymmetricEncrypt and SymmetricDecrypt methods.

------ EDIT ------

As noted in the comment below, we are not allowed to alter the Encrypt method. I do like a good challenge! With appropriate byte mangling applied, here's a decrypt that honours the return coming form the Encrypt method:

public static string Decrypt(string disguisedtext)
{
    byte[] disguishedtextBytes = FromHexString(disguisedtext);

    var originalLength = disguishedtextBytes.Length;

    int BlockSize;
    BlockSize = 16 * (1 + (originalLength / 16));
    Array.Resize(ref disguishedtextBytes, BlockSize);

    // fill the remaining space with 0
    for (int i = originalLength; i < BlockSize; i++)
    {
        disguishedtextBytes[i] = 0;
    }


    byte[] rgbIV;
    byte[] key;

    BuildRigndaelCommon(out rgbIV, out key);    

    string visiabletext = "";
    //create uninitialized Rijndael encryption obj
    using (var symmetricKey = new RijndaelManaged())
    {
            //Call SymmetricAlgorithm.CreateEncryptor to create the Encryptor obj
        symmetricKey.Mode = CipherMode.CFB;
        symmetricKey.BlockSize = 128;
        symmetricKey.Padding = PaddingMode.None;        

            // ICryptoTransform encryptor = symmetricKey.CreateEncryptor(key, rgbIV);
        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(key, rgbIV);

            //define memory stream to hold encrypted data
        using (MemoryStream ms = new MemoryStream(disguishedtextBytes))
        using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
        {
            byte[] decryptedData = new byte[disguishedtextBytes.Length];
            int stringSize = cs.Read(decryptedData, 0, disguishedtextBytes.Length);
            cs.Close();

                //Trim the excess empty elements from the array and convert back to a string
            byte[] trimmedData = new byte[stringSize];
            Array.Copy(decryptedData, trimmedData, originalLength); 
            Array.Resize(ref trimmedData, originalLength);

            visiabletext = Encoding.UTF8.GetString(trimmedData);        
        }
    }
    return visiabletext;
}