繁体   English   中英

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

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

我正在尝试在 C# 中加密一个通用流。 虽然程序没有问题,但是加密解密转换为字符串时返回空白。 任何帮助表示赞赏。

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


        }
    }
}

问题可能出在我从流中读取和写入流时。

这段代码有很多问题。

  1. 什么都没有解密的原因是因为您在执行message.CopyTo(memoryStream)之前忘记重置message流,因为CopyTo从当前位置开始工作,并且您在解密后没有更改位置。

    你可以用

    message.Position = 0;
  2. 如果对任意数据进行加密,具有某种操作模式(如 CBC)的 AES 是不够的。 我们通常需要某种填充方案。 在 C# 中,默认方案是 PKCS#7 填充。 即使明文已经是块大小的倍数,也总是添加明确的填充。 在这些情况下,会添加一个完整的填充块。

    现在的问题是您在加密和解密期间读取了 2048 字节的块,但加密产生了 2064 字节的密文块,在解密期间必须这样读取。 这是一个简单的修复,但最好一直使用流而不是加密这些单独的块。

  3. 您为每 2048 字节块调用Rfc2898DeriveBytes ,但它永远不会改变。 要么引入随机性,但实际上使用随机盐和随机 IV,要么缓存密钥。 (随机盐和随机IV仍然需要达到语义安全)

暂无
暂无

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

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