简体   繁体   English

使用 RijndaelManaged 在 C# 中加密/解密流

[英]Encrypt/Decrypt Stream in C# using RijndaelManaged

I am trying to encrypt a Generic Stream in C#.我正在尝试在 C# 中加密一个通用流。 Although the program has no problems, the encryption and decryption return blank when converted to strings.虽然程序没有问题,但是加密解密转换为字符串时返回空白。 Any help is appreciated.任何帮助表示赞赏。

    public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
        {
            byte[] encryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                        cs.Close();
                    }
                    encryptedBytes = ms.ToArray();
                }
            }

            return encryptedBytes;
        }

        public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
        {
            byte[] decryptedBytes = null;

            // Set your salt here, change it to meet your flavor:
            // The salt bytes must be at least 8 bytes.
            byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

            using (MemoryStream ms = new MemoryStream())
            {
                using (RijndaelManaged AES = new RijndaelManaged())
                {
                    AES.KeySize = 256;
                    AES.BlockSize = 128;

                    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
                    AES.Key = key.GetBytes(AES.KeySize / 8);
                    AES.IV = key.GetBytes(AES.BlockSize / 8);

                    AES.Mode = CipherMode.CBC;

                    using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                        cs.Close();
                    }
                    decryptedBytes = ms.ToArray();
                }
            }

            return decryptedBytes;
        }

        private void Encrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Encrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        private void Decrypt(Stream input, Stream output, String password)
        {
            input.Position = 0;
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            using (var stream = new MemoryStream())
            {
                byte[] buffer = new byte[2048]; // read in chunks of 2KB
                int bytesRead;
                while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, bytesRead);
                    var tmp = AES_Decrypt(buffer, passwordBytes);
                    output.Write(tmp, 0, tmp.Length);
                }
            }

        }

        static void Main(string[] args)
        {

            Program obj = new Program();
            var message = new MemoryStream();
            var cipher = new MemoryStream();
            string tmp = "This is a test if the encryption is working!";

            StreamWriter sw = new StreamWriter(message);
            sw.Write(tmp);

            obj.Encrypt(message, cipher, "password");

            cipher.Position = 0;
            message = new MemoryStream();

            obj.Decrypt(cipher, message, "password");

            using (var memoryStream = new MemoryStream())
            {
                message.CopyTo(memoryStream);
                var bytesdecrypt = memoryStream.ToArray();
                string result = Encoding.UTF8.GetString(bytesdecrypt);
                Console.WriteLine(result);
                Console.ReadLine();
            }


        }
    }
}

The problem is probably when I am reading and writing from and to the streams.问题可能出在我从流中读取和写入流时。

There are many problems with this code.这段代码有很多问题。

  1. The reason why nothing is decrypted is because you forgot to reset the message stream before doing message.CopyTo(memoryStream) , because CopyTo works from the current position and you haven't changed the position after decryption.什么都没有解密的原因是因为您在执行message.CopyTo(memoryStream)之前忘记重置message流,因为CopyTo从当前位置开始工作,并且您在解密后没有更改位置。

    You could reset it with你可以用

    message.Position = 0;
  2. If arbitrary data is encrypted, AES with some mode of operation like CBC is not enough.如果对任意数据进行加密,具有某种操作模式(如 CBC)的 AES 是不够的。 We generally need some kind of padding scheme.我们通常需要某种填充方案。 In C# the default scheme is PKCS#7 padding.在 C# 中,默认方案是 PKCS#7 填充。 Unambiguous padding is always added even when the plaintext is already a multiple of the block size.即使明文已经是块大小的倍数,也总是添加明确的填充。 In those cases a full padding block is added.在这些情况下,会添加一个完整的填充块。

    Now the problem is that you're reading 2048 byte chunks during encryption and decryption, but the encryption produces 2064 byte ciphertext chunks which must be read as such during decryption.现在的问题是您在加密和解密期间读取了 2048 字节的块,但加密产生了 2064 字节的密文块,在解密期间必须这样读取。 This is a simple fix, but it would be better to use streams all the way instead of encrypting such separate chunks.这是一个简单的修复,但最好一直使用流而不是加密这些单独的块。

  3. You're invoking Rfc2898DeriveBytes for every 2048 byte chunk, but it never changes.您为每 2048 字节块调用Rfc2898DeriveBytes ,但它永远不会改变。 Either introduce randomness, but actually using a random salt and random IV, or cache the key.要么引入随机性,但实际上使用随机盐和随机 IV,要么缓存密钥。 (random salt and random IV are still necessary to reach semantic security) (随机盐和随机IV仍然需要达到语义安全)

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

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