我需要对文件流进行加密和解密,以便写入文件系统的数据已经加密,并且只有在读取后才能在内存中解密。
我写了一个小型演示程序:
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。可能的原因是序列化和反序列化之间的流或对象版本更改无效。'
现在我的想法已经用完了,有没有人看到这个问题?
谢谢!
答案 0 :(得分:4)
您使用不同的密钥进行加密和解密。以下几行:
var password = new Rfc2898DeriveBytes(PassPhrase, 100);
var keyBytes = password.GetBytes(32);
生成指定大小的随机盐(100字节),然后从密码和随机盐中派生出给定大小的密钥。这意味着当执行两次时 - 这将产生两个不同的密钥 - 因此在您的情况下解密失败。
所以解密你需要有一个用于加密的密钥(在你的代码中 - 例如通过加密和解密块移动密钥生成)或者你需要用于加密的passphase和salt(你可以使用password.Salt
属性来获取和设置salt,或者你可以将它传递给Rfc2898DeriveBytes
的构造函数。