如何在序列化后加密并保存二进制流并将其读回?

时间:2011-01-03 11:41:54

标签: c# .net serialization cryptography

当我想在二进制序列化后加密二进制流并将其保存到文件时,我在使用CryptoStream时遇到一些问题。我收到以下异常

System.ArgumentException : Stream was not readable.

有人可以告诉我如何加密二进制流并将其保存到文件中并正确反序列化吗?

代码如下:

class Program
    {
        public static void Main(string[] args)
        {
            var b = new B {Name = "BB"};
            WriteFile<B>(@"C:\test.bin", b, true);
            var bb = ReadFile<B>(@"C:\test.bin", true);

            Console.WriteLine(b.Name == bb.Name);
            Console.ReadLine();
        }

        public static T ReadFile<T>(string file, bool decrypt)
        {
            T bObj = default(T);
            var _binaryFormatter = new BinaryFormatter();
            Stream buffer = null;

            using (var stream = new FileStream(file, FileMode.OpenOrCreate))
            {
                if(decrypt)
                {
                    const string strEncrypt = "*#4$%^.++q~!cfr0(_!#$@$!&#&#*&@(7cy9rn8r265&$@&*E^184t44tq2cr9o3r6329";
                    byte[] dv = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
                    CryptoStream cs;
                    DESCryptoServiceProvider des = null;

                    var byKey = Encoding.UTF8.GetBytes(strEncrypt.Substring(0, 8));
                    using (des = new DESCryptoServiceProvider())
                    {
                        cs = new CryptoStream(stream, des.CreateEncryptor(byKey, dv), CryptoStreamMode.Read);
                    }
                    buffer = cs;
                }
                else
                    buffer = stream;
                try
                {
                    bObj = (T) _binaryFormatter.Deserialize(buffer);
                }
                catch(SerializationException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }

            return bObj;
        }

        public static void WriteFile<T>(string file, T bObj, bool encrypt)
        {
            var _binaryFormatter = new BinaryFormatter();
            Stream buffer;

            using (var stream = new FileStream(file, FileMode.Create))
            {
                try
                {
                    if(encrypt)
                    {
                        const string strEncrypt = "*#4$%^.++q~!cfr0(_!#$@$!&#&#*&@(7cy9rn8r265&$@&*E^184t44tq2cr9o3r6329";
                        byte[] dv = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
                        CryptoStream cs;
                        DESCryptoServiceProvider des = null;

                        var byKey = Encoding.UTF8.GetBytes(strEncrypt.Substring(0, 8));
                        using (des = new DESCryptoServiceProvider())
                        {
                            cs = new CryptoStream(stream, des.CreateEncryptor(byKey, dv), CryptoStreamMode.Write);
                            buffer = cs;
                        }
                    }
                    else
                        buffer = stream;

                    _binaryFormatter.Serialize(buffer, bObj);
                    buffer.Flush();
                }
                catch(SerializationException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

    [Serializable]
    public class B
    {
        public string Name {get; set;}
    }

它抛出序列化异常,如下所示

输入流不是有效的二进制格式。起始内容(以字节为单位) 是:3F-17-2E-20-80-56-A3-2A-46-63-22-C4-49-56-22-B4-DA ...

2 个答案:

答案 0 :(得分:4)

如果你这样做,它应该有效:

// A: encrypting when writing
// 1. create backing storage stream. In your case a file stream
using(Stream innerStream = File.Create(path))
// 2. create a CryptoStream in write mode
using(Stream cryptoStream = new CryptoStream(innerStream, encryptor, CryptoStreamMode.Write))
{
    // 3. write to the cryptoStream
    binaryFormatter.Serialize(cryptoStream, obj);
}

// B: decrypting when reading
// 1. create backing storage stream. In your case a file stream
using(Stream innerStream = File.Open(path, FileMode.Open))
// 2. create a CryptoStream in read mode
using(Stream cryptoStream = new CryptoStream(innerStream, decryptor, CryptoStreamMode.Read))
{
    // 3. read from the cryptoStream
    obj = binaryFormatter.Deserialize(cryptoStream);
}

您的代码存在一些问题:

  1. 您在阅读时使用的是加密器。这可能是一个错字,但它应该是一个解密器。

  2. 您正在刷新buffer,但使用CryptoStream时这还不够。加密器和解密器适用于固定大小的块。最后一个块可能没有那么大,所以需要特殊处理。最后一个块是在流关闭之前写入的块,而不是刷新。刷新CryptoStream没有任何用处,因为它不能写任何大小小于加密器/解密器的输入块大小的东西,除非它是最后写的东西。除此之外,总的来说,无论如何,你都应该关闭你的溪流。建议使用using语句:

    using(buffer)
        _binaryFormatter.Serialize(buffer, bObj);
    

答案 1 :(得分:1)

在MSDN文档中有一个很好的示例如何执行此操作: CryptoStream MSDN它位于“示例”部分。

程序基本上是这样的:

  1. 创建cryptostream(空流)
  2. 将内容写入cryptostream(加密)
  3. 将cryptostream保存到文件
  4. 从文件内容创建cryptostream
  5. 从cryptostream读取(解密)