加密.NET二进制序列化流

时间:2015-03-01 07:27:52

标签: c# encryption

我在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));
}

谢谢!

2 个答案:

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