在C#中使用Rijndael进行解密

时间:2013-07-05 07:21:14

标签: c# encryption cryptography 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);

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

你可以看到它非常标准,除了最后它被转换为十六进制并执行子串。我正在做相反的事情。

我的解密方法如下:

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

助手方法:

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

我遇到有关字符串长度的各种错误,并且填充无效。有任何想法让解密工作。我已经尝试将输入字符串填充回32字节,但没有用。

2 个答案:

答案 0 :(得分:4)

您的问题是加密方法中的一个细微错误。通过弄乱hexOutput字符串,您丢失了返回的密文中的数据。而不是:

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

你应该只返回输出:

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

您还需要将Decrypt方法中的填充模式更改为None。虽然现在可以正确解密,但它还包括您在加密方法中添加的手动填充字符。由于你不知道你的纯文本,你没有很好的方法来删除它们。您总是可以添加一个方法来删除数组中与填充值零不匹配的所有字节:

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

然而,这并不是一个好主意,因为您可能会丢弃其他有效数据。更好的解决方案是更新加密和解密方法,让密码处理填充。把它们放在一起我们得到(只显示我已经改变了):

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

希望这有助于指明你的方向。另外,我在Snipt上维护a set of encryption utilities可能对你有用,特别是SymmetricEncrypt和SymmetricDecrypt方法。

------编辑------

如下面的评论中所述,我们不允许更改加密方法。我确实喜欢挑战!通过应用适当的字节修改,这里有一个解密来表示加密方法的返回:

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

答案 1 :(得分:0)

看起来您的加密方法输出一个空格分隔的十六进制字符串,表示一个字节数组:“OA FE 82 3B ...”。它还对明文进行了假设,并对任何填充进行了删除。

你的第一步是将十六进制字符串转换回字节数组,这很容易。

为了处理丢失的填充,只需将解密设置为NoPadding,正如@Wolfwyrd所说。如果填充长度关闭,您可能必须检查数据是否正确终止。

如果关于明文字符的假设是错误的,那么很可能你必须手工恢复。如果明文是严格的ASCII(仅7位字符),那么这应该不是问题。除此之外的任何东西,例如重音字母:á,é等都会打破这种假设。