简体   繁体   English

在C#中使用Rijndael进行解密

[英]Decryption using Rijndael in C#

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. 我试过将输入字符串填充回32字节,但无济于事。

Your problem is a subtle error in your Encrypt method. 您的问题是Encrypt方法中的一个细微错误。 You are losing data from your returned ciphertext by messing with the the hexOutput string. 通过将hexOutput字符串弄乱,您正在从返回的密文中丢失数据。 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. 您还需要将Decrypt方法中的填充模式更改为“无”。 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. 顺便说一句,我在Snipt上维护了一组可能对您有用的加密实用程序 ,尤其是SymmetricEncrypt和SymmetricDecrypt方法。

------ EDIT ------ ------编辑------

As noted in the comment below, we are not allowed to alter the Encrypt method. 如以下评论中所述,我们不允许更改Encrypt方法。 I do like a good challenge! 我确实很喜欢挑战! With appropriate byte mangling applied, here's a decrypt that honours the return coming form the Encrypt method: 应用适当的字节处理后,下面的解密将纪念Encrypt方法的返回:

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;
}

It looks like your encryption method outputs a space separated hex string, representing a byte array: "OA FE 82 3B ...". 看起来您的加密方法输出了一个空格分隔的十六进制字符串,表示一个字节数组:“ OA FE 82 3B ...”。 It also makes assumptions about the plaintext and chops off any padding. 它还对纯文本进行了假设,并删除了任何填充。

Your first step it to convert the hex string back into a byte array, which is pretty easy. 第一步是将十六进制字符串转换回字节数组,这非常简单。

To deal with the lost padding just set decryption to NoPadding , as @Wolfwyrd suggests. 要处理丢失的填充,只需将解密设置为NoPadding ,如@Wolfwyrd所建议的那样。 You may have to check that your data is correctly terminated if the padding length was off. 如果填充长度不正确,则可能必须检查数据是否正确终止。

If the assumptions about plaintext characters were wrong, then it is likely you will have to recover things by hand. 如果有关纯文本字符的假设是错误的,那么您很可能必须手动恢复。 If the plaintext is strict ASCII (7 bit characters only) then this should not be a problem. 如果明文是严格的ASCII(仅7位字符),那么这应该不是问题。 Anything outside that, such as accented letters: á, é etc. will break the assumption. 超出此范围的任何内容(例如带重音的字母á,é等)都会破坏假设。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM