加密.NET二进制序列化流

Raf*_*ida 3 c# encryption

我正在用C#学习加密,我遇到了麻烦.我有一些Rijndael加密代码,它与字符串完美配合.但是现在我正在研究序列化并且BinaryWriter没有任何保护地写入类的数据.我正在使用此代码进行测试 ; 有没有办法"加密类",或类似的东西?

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

FileStream file = new FileStream(Environment.CurrentDirectory + @"\class.dat", FileMode.Create);
using (BinaryWriter sw = new BinaryWriter(file))
{
    byte[] byt = ConverteObjectEmByte(myVarClass);
    sw.Write(byt);
}
Run Code Online (Sandbox Code Playgroud)

这就是我读它的方式:

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));
}
Run Code Online (Sandbox Code Playgroud)

谢谢!

sof*_*ess 8

byte[]传递给不同的流对象时,您可以将多个流链接在一起,将输出从一个传递到另一个的输入,而不是转换为中间步骤.

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

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

考虑到这一点,您可以改为ConvertObjectEmByte:

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

    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(outputStream, obj);
}
Run Code Online (Sandbox Code Playgroud)

同样,ConvertByteEmObject可以成为:

public static object ReadObjectFromStream(Stream inputStream)
{
    BinaryFormatter binForm = new BinaryFormatter();
    object obj = binForm.Deserialize(inputStream);
    return obj;
}
Run Code Online (Sandbox Code Playgroud)

要添加加密/解密,我们可以编写创建CryptoStream对象的函数,我们可以使用这些二进制序列化函数进行链接.我下面的示例函数看起来与您链接的文章中的Encrypt/ Decryptfunctions 略有不同,因为IV(初始化向量)现在是随机生成的并写入流(并从另一端的流中读取).重要的是,IV对于您为安全性而加密的每个数据块都是唯一的,并且您还应该使用用于加密目的的随机数生成器RNGCryptoServiceProvider,而不是像伪随机数生成器那样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;
}
Run Code Online (Sandbox Code Playgroud)

最后,我们可以把它粘在一起:

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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们将file流对象传递给CreateEncryptionStream(和CreateDecryptionStream),然后将cryptoStream对象传递给WriteObjectToStream(和ReadObjectfromStream).您还会注意到流在using块内部,因此当我们完成它们时它们将自动清理.

这是完整的测试程序:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是个好问题.对于为安全性而加密的每个文件,IV应该是唯一的(不要再次使用相同的IV).我已经更新了示例,以显示它是随机生成并写入文件并从文件中读取,这是通常的方法. (2认同)