C# 加密.NET二进制序列化流

C# 加密.NET二进制序列化流,c#,encryption,C#,Encryption,我在学C#的加密技术,我遇到了麻烦。我有一些,它的工作与字符串完美。但是现在我正在研究序列化,BinaryWriter在没有任何保护的情况下编写类的数据。我正在使用;有没有“加密类”或类似的方法 为了澄清这个问题,以下是我的代码: FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Create); using (BinaryWriter sw = new BinaryWr

我在学C#的加密技术,我遇到了麻烦。我有一些,它的工作与字符串完美。但是现在我正在研究序列化,
BinaryWriter
在没有任何保护的情况下编写类的数据。我正在使用;有没有“加密类”或类似的方法

为了澄清这个问题,以下是我的代码:

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

谢谢

当传递到不同的流对象时,您可以将多个流链接在一起,将输出从一个流传递到另一个流的输入,而不是转换为
字节[]

这种方法在这里是有意义的,因为你是链接在一起的

二进制序列化=>加密=>写入文件

记住这一点,您可以将
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
对象,这些对象可以与这些二进制序列化函数链接。下面的示例函数看起来与您链接到的文章中的
加密
/
解密
函数有些不同,因为现在随机生成并写入流(并从另一端的流读取)。重要的是,为安全起见,您加密的每个数据块的IV都是唯一的,您还应该使用用于加密目的的随机数生成器,如,而不是伪随机数生成器,如
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);
}
请注意,我们将
文件
流对象传递给
CreateEncryptionStream
(和
CreateDecryptionStream
),然后将
加密流
对象传递给
WriteObjectToStream
(和
ReadObjectfromStream
)。您还将注意到,流的作用域是使用块限定在
内的,因此,当我们完成它们时,它们将自动被清除

以下是完整的测试程序:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;

namespace CryptoStreams
{
    class Program
    {
        [Serializable]
        public class MyClass
        {
            public string TestValue
            {
                get;
                set;
            }

            public int SomeInt
            {
                get;
                set;
            }
        }

        public static void WriteObjectToStream(Stream outputStream, Object obj)
        {
            if (object.ReferenceEquals(null, obj))
            {
                return;
            }

            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(outputStream, obj);
        }

        public static object ReadObjectFromStream(Stream inputStream)
        {
            BinaryFormatter binForm = new BinaryFormatter();
            object obj = binForm.Deserialize(inputStream);
            return obj;
        }

        private const string cryptoKey =
            "Q3JpcHRvZ3JhZmlhcyBjb20gUmluamRhZWwgLyBBRVM=";
        private const int keySize = 256;
        private const int ivSize = 16; // block size is 128-bit

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

        static void Main(string[] args)
        {
            MyClass myVarClass = new MyClass
            {
                SomeInt = 1234,
                TestValue = "Hello"
            };

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

            Console.WriteLine("newMyVarClass.SomeInt: {0}; newMyVarClass.TestValue: {1}",
                newMyVarClass.SomeInt,
                newMyVarClass.TestValue);
        }
    }
}

我不确定.Net库是否已更改,或者只是代码错误。我不能直接运行软件编写的代码。 从那以后,我根据答案修改了代码,以便正确使用。这里有一个例子

公共类加密序列化
{
公共静态void WriteObjectToStream(Stream outputStream,object obj)
{
如果(obj为null)抛出新ArgumentNullException(“obj不能为null”);
BinaryFormatter bf=新的BinaryFormatter();
序列化(outputStream,obj);
}
公共静态对象ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf=新的BinaryFormatter();
返回bf.反序列化(inputStream);
}
公共静态加密流CreateEncryptionStream(流输出流,字节[]密钥,字节[]IV)
{
Rijndael Rijndael=新的RijndaelManaged();
返回新的加密流(outputStream,rijndael.CreateEncryptor(Key,IV),CryptoStreamMode.Write);
}
公共静态加密流CreateDecryptionStream(流输入流,字节[]密钥,字节[]IV)
{
Rijndael Rijndael=新的RijndaelManaged();
返回新的加密流(inputStream,rijndael.CreateDecryptor(Key,IV),CryptoStreamMode.Read);
}
公共静态void EncryptObjectToFile(对象对象对象,字符串路径,字节[]键,字节[]IV)
{
使用FileStream file=newfilestream(路径,FileMode.Create);
使用(CryptoStream CryptoStream=CreateEncryptionStream(文件、密钥、IV))
{
WriteObjectToStream(cryptoStream,obj);
}
}
公共静态对象DecryptObjectFromFile(字符串路径,字节[]密钥,字节[]IV)
{
使用FileStream file=newfilestream(路径,FileMode.Open);
使用(CryptoStream CryptoStream=CreateDecryptionStream(文件、密钥、IV))
{
返回ReadObjectFromStream(cryptoStream);
}
}
}
[可序列化]
公立班学生
{
公共字符串名称;
公共信息;
}
静态异步任务主(字符串[]args)
{
//原始字符串“[这是一个示例键字符串!]”;
//我不知道字符串的长度是否必须是32,但当我尝试64时,它出错了。
字符串cryptoKey=“W1ROAXMGYW4GZXHHBXBSSBRZXKGC3RYAW5NIV0=”;
字节[]键=Convert.FromBase64String(加密键);
字节[]IV=新字节[16];
使用(RNGCryptoServiceProvider rngcsp=new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//同
//Rijndael Rijndael=新的RijndaelManaged();
//rijndael.GenerateIV();
//字节[]iv=rijndael.iv;
列出学生=新名单(){新学生{Name=“John”,年龄=10},新学生{Name=“Marry”,年龄=15};
CryptoSerialization.EncryptObjectToFile(学生,Environment.CurrentDirectory+@“\testCrypto.dat”,密钥,IV);
List newStudents=(List)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory+@“\testCrypto.dat”,Key,IV);
newStudents.ForEach((stu)=>
{
控制台写入线(stu.Name+,“+stu.Age);
});
Console.ReadKey();
}

您可以在ConverteObject和ConverteByteObject内部使用。在链接中有Rijndael的例子…非常感谢!!我在这里测试过,它在多个类中都能完美地工作。只有一个问题,出于安全原因,我更改了加密密钥,但我不知道这是什么bIV,这是可变的吗?再次感谢这是个好问题。为安全起见,您加密的每个文件的IV都应该是唯一的(不要再次使用相同的IV)。我已经更新了示例,以显示它是随机生成的,并写入和读取文件,这将是通常的方法。我尝试了更改,但我不知道如何生成新的IV。如何做?您看到我的示例的更新了吗,我在其中随机生成了IV?