我在C#学习加密,我遇到了麻烦。我有一些Rijndael encryption code,它与字符串完美配合。但现在我正在学习序列化,而BinaryWriter
在没有任何保护的情况下写出类的数据。我使用this code to test;有没有办法加密课程"或类似的东西?
澄清问题,这是我的代码:
FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Create);
using (BinaryWriter sw = new BinaryWriter(file))
{
byte[] byt = ConverteObjectEmByte(myVarClass);
sw.Write(byt);
}
这就是我读它的方式:
MyClass newMyVarClass;
FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Open);
using (BinaryReader sr = new BinaryReader(file))
{
// 218 is the size of the byte array that I've tested (byt)
myNewVarClass = (MyClass)ConverteByteEmObject(sr.ReadBytes(218));
}
谢谢!
答案 0 :(得分:6)
传递给不同的流对象时,不是转换为byte[]
作为中间步骤,而是可以将多个流链接在一起,将输出从一个传递到另一个的输入。
这种方法在这里有意义,因为你在一起链接
二进制序列化 => 加密 => 写入文件。
考虑到这一点,您可以将ConvertObjectEmByte
更改为:
public static void WriteObjectToStream(Stream outputStream, Object obj)
{
if (object.ReferenceEquals(null, obj))
{
return;
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
同样,ConvertByteEmObject
可以成为:
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter binForm = new BinaryFormatter();
object obj = binForm.Deserialize(inputStream);
return obj;
}
要添加加密/解密,我们可以编写创建CryptoStream
对象的函数,我们可以使用这些二进制序列化函数进行链接。下面的示例函数与您链接的文章中的Encrypt
/ Decrypt
函数略有不同,因为现在IV (Initialization Vector)随机生成并写入流(并从流中读取)在另一端)。重要的是,对于为安全性而加密的每个数据块,IV是唯一的,并且您还应该使用用于加密目的的随机数生成器,如RNGCryptoServiceProvider
,而不是像{{1}这样的伪随机数生成器。 }。
Random
最后,我们可以把它粘在一起:
public static CryptoStream CreateEncryptionStream(byte[] key, Stream outputStream)
{
byte[] iv = new byte[ivSize];
using (var rng = new RNGCryptoServiceProvider())
{
// Using a cryptographic random number generator
rng.GetNonZeroBytes(iv);
}
// Write IV to the start of the stream
outputStream.Write(iv, 0, iv.Length);
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream encryptor = new CryptoStream(
outputStream,
rijndael.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
return encryptor;
}
public static CryptoStream CreateDecryptionStream(byte[] key, Stream inputStream)
{
byte[] iv = new byte[ivSize];
if (inputStream.Read(iv, 0, iv.Length) != iv.Length)
{
throw new ApplicationException("Failed to read IV from stream.");
}
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream decryptor = new CryptoStream(
inputStream,
rijndael.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
return decryptor;
}
请注意,我们将byte[] key = Convert.FromBase64String(cryptoKey);
using (FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Create))
using (CryptoStream cryptoStream = CreateEncryptionStream(key, file))
{
WriteObjectToStream(cryptoStream, myVarClass);
}
MyClass newMyVarClass;
using (FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Open))
using (CryptoStream cryptoStream = CreateDecryptionStream(key, file))
{
newMyVarClass = (MyClass)ReadObjectFromStream(cryptoStream);
}
流对象传递给file
(和CreateEncryptionStream
),然后将CreateDecryptionStream
对象传递给cryptoStream
(和{{ 1}})。您还会注意到这些流的作用域位于WriteObjectToStream
块内,因此当我们完成它们时它们会自动清理。
这是完整的测试程序:
ReadObjectfromStream
答案 1 :(得分:0)
我不确定.Net库是否已更改,或者只是代码错误。我不能直接运行softwariness编写的代码。 从那以后,我根据答案更改了代码,以便可以正确使用它。这是一个例子。
public class CryptoSerialization
{
public static void WriteObjectToStream(Stream outputStream, object obj)
{
if (obj is null) throw new ArgumentNullException("obj can't be null");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(inputStream);
}
public static CryptoStream CreateEncryptionStream(Stream outputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(outputStream, rijndael.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
}
public static CryptoStream CreateDecryptionStream(Stream inputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(inputStream, rijndael.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
}
public static void EncryptObjectToFile(object obj, string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Create);
using (CryptoStream cryptoStream = CreateEncryptionStream(file, Key, IV))
{
WriteObjectToStream(cryptoStream, obj);
}
}
public static object DecryptObjectFromFile(string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Open);
using (CryptoStream cryptoStream = CreateDecryptionStream(file, Key, IV))
{
return ReadObjectFromStream(cryptoStream);
}
}
}
[Serializable]
public class Student
{
public string Name;
public int Age;
}
static async Task Main(string[] args)
{
// the original string "[This is an example key string!]";
// I don't know if the length of the string has to be 32, but when I tried 64, it went wrong.
string cryptoKey = "W1RoaXMgaXMgYW4gZXhhbXBsZSBrZXkgc3RyaW5nIV0=";
byte[] Key = Convert.FromBase64String(cryptoKey);
byte[] IV = new byte[16];
using (RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//same as
//Rijndael rijndael = new RijndaelManaged();
//rijndael.GenerateIV();
//byte[] iv = rijndael.IV;
List<Student> students = new List<Student>() { new Student { Name = "John", Age = 10 }, new Student { Name = "Marry", Age = 15 } };
CryptoSerialization.EncryptObjectToFile(students, Environment.CurrentDirectory + @"\testCrypto.dat", Key, IV);
List<Student> newStudents = (List<Student>)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory + @"\testCrypto.dat", Key, IV);
newStudents.ForEach((stu) =>
{
Console.WriteLine(stu.Name + ", " + stu.Age);
});
Console.ReadKey();
}