How to encrypt and save a binary stream after serialization and read it back?

|▌冷眼眸甩不掉的悲伤 提交于 2020-01-01 05:24:10

问题


I am having some problems in using CryptoStream when I want to encrypt a binary stream after binary serialization and save it to a file. I am getting the following exception

System.ArgumentException : Stream was not readable.

Can anybody please show me how to encrypt a binary stream and save it to a file and deserialize it back correctly?

The code is as follows:

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

It throws the serialization exception as follows

The input stream is not a valid binary format. The starting contents (in bytes) are: 3F-17-2E-20-80-56-A3-2A-46-63-22-C4-49-56-22-B4-DA ...


回答1:


If you do it like this, it should work:

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

There are a couple of problems with your code:

  1. You're using an encryptor when reading. This was probably a typo, but it should be a decryptor.

  2. You are flushing the buffer, but that is not enough when using a CryptoStream. Encryptors and decryptors work on blocks of a fixed size. The last block may not have that size, so it needs special treatment. The last block is the one written before the stream is closed, not flushed. Flushing on a CryptoStream does nothing useful, because it cannot write anything of size less than the input block size of the encryptor/decryptor, unless it is the last thing to be written. And on top of this, in general you should always close your streams, no matter what. The using statement is the recommended way of doing it:

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



回答2:


There is a great example on how to do this in the MSDN documentation: CryptoStream MSDN it's in the "Examples" section.

The procedure is basically this:

  1. create cryptostream (empty stream)
  2. write contents to cryptostream (encrypt)
  3. save cryptostream to file
  4. create cryptostream from file contents
  5. read from cryptostream (decrypt)


来源:https://stackoverflow.com/questions/4584135/how-to-encrypt-and-save-a-binary-stream-after-serialization-and-read-it-back

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!