C#加密和解密文件流:System.Runtime.Serialization.SerializationException

时间:2018-02-26 08:35:25

标签: c# encryption

我需要对文件流进行加密和解密,以便写入文件系统的数据已经加密,并且只有在读取后才能在内存中解密。

我写了一个小型演示程序:

using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace House
{
    public partial class Form1 : Form
    {
        public static string PassPhrase => "JustATest";
        public static string PathToFile = @"C:\TMP\test.bin";

        public Form1()
        {
            InitializeComponent();

            var iv = new byte[]
            {
                222, 49, 46, 125, 250, 194, 122, 241, 234, 22, 151, 76, 181, 83, 149, 92, 195, 11, 146, 213, 195, 40, 0,
                183, 84, 149, 185, 100, 250, 156, 123, 75
            };

            if (File.Exists(PathToFile))
                File.Delete(PathToFile);

            var set = CreateData();

            using (var symmetricKey = new RijndaelManaged())
            {
                var password = new Rfc2898DeriveBytes(PassPhrase, 100);
                var keyBytes = password.GetBytes(32);

                symmetricKey.BlockSize = 256;
                symmetricKey.Mode = CipherMode.CBC;
                symmetricKey.Padding = PaddingMode.PKCS7;
                var encryptor = symmetricKey.CreateEncryptor(keyBytes, iv);

                using (var fileStream = new FileStream(PathToFile, FileMode.Create))
                {
                    using (var cryptoStream = new CryptoStream(fileStream, encryptor, CryptoStreamMode.Write))
                    {
                        var formatter = new BinaryFormatter();
                        formatter.Serialize(cryptoStream, set);
                        cryptoStream.FlushFinalBlock();
                        fileStream.Flush(true);
                    }
                }
            }

            using (var symmetricKey = new RijndaelManaged())
            {
                var password = new Rfc2898DeriveBytes(PassPhrase, 100);
                var keyBytes = password.GetBytes(32);

                symmetricKey.BlockSize = 256;
                symmetricKey.Mode = CipherMode.CBC;
                symmetricKey.Padding = PaddingMode.PKCS7;
                var decryptor = symmetricKey.CreateDecryptor(keyBytes, iv);

                using (var fileStream = new FileStream(PathToFile, FileMode.Open, FileAccess.Read))
                {
                    using (var cryptoStream = new CryptoStream(fileStream, decryptor, CryptoStreamMode.Read))
                    {
                        var formatter = new BinaryFormatter();
                        var deserialized = (DataSet)formatter.Deserialize(cryptoStream);
                    }
                }
            }
        }

        private static DataSet CreateData()
        {
            var customer = new DataTable("customer");
            var customerId = customer.Columns.Add("Id", typeof(int));
            customer.Columns.Add("Name", typeof(string));

            var order = new DataTable("orders");
            order.Columns.Add("Id", typeof(int));
            var fkCustomerId = order.Columns.Add("FkCustomerId", typeof(int));
            order.Columns.Add("Name", typeof(string));

            var set = new DataSet();
            set.Tables.Add(customer);
            set.Tables.Add(order);
            set.Relations.Add(new DataRelation("r1", customerId, fkCustomerId));

            var r1 = set.Tables["customer"].NewRow();
            r1[0] = 5;
            r1[1] = "Test";
            set.Tables["customer"].Rows.Add(r1);

            var r2 = set.Tables["orders"].NewRow();
            r2[0] = 1;
            r2[1] = 5;
            r2[2] = "test";
            set.Tables["orders"].Rows.Add(r2);
            return set;
        }
    }
}

当我运行程序时,一旦涉及数据反序列化就抛出以下异常: System.Runtime.Serialization.SerializationException: '二进制流' XX'不包含有效的BinaryHeader。可能的原因是序列化和反序列化之间的流或对象版本更改无效。'

现在我的想法已经用完了,有没有人看到这个问题?

谢谢!

1 个答案:

答案 0 :(得分:4)

您使用不同的密钥进行加密和解密。以下几行:

var password = new Rfc2898DeriveBytes(PassPhrase, 100);                
var keyBytes = password.GetBytes(32);

生成指定大小的随机盐(100字节),然后从密码和随机盐中派生出给定大小的密钥。这意味着当执行两次时 - 这将产生两个不同的密钥 - 因此在您的情况下解密失败。

所以解密你需要有一个用于加密的密钥(在你的代码中 - 例如通过加密和解密块移动密钥生成)或者你需要用于加密的passphase和salt(你可以使用password.Salt属性来获取和设置salt,或者你可以将它传递给Rfc2898DeriveBytes的构造函数。