Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
368 views
in Technique[技术] by (71.8m points)

c# - Encrypt .NET binary serialization stream

I'm studying encryption in C# and I'm having trouble. I have some Rijndael encryption code and it's working perfectly with strings. But now I'm studying serialization and the BinaryWriter writes the data of classes without any protection. I'm using this code to test; is there a way to "encrypt the class", or something similar?

To clarify the question, here is my code:

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

And this is how I read it:

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

Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Rather than converting to byte[] as an intermediate step when passing to different stream objects you can chain multiple streams together, passing the output from one to the input of another.

This approach makes sense here, as you are chaining together

Binary Serialization => Encryption => Writing to File.

With this in mind, you can change ConvertObjectEmByte to something like:

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

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

and similarly, ConvertByteEmObject can become:

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

To add in the encryption/decryption, we can write functions that create CryptoStream objects that we can chain with these binary serialization functions. My example functions below look a bit different from the Encrypt/Decrypt functions in the article you linked to because the IV (Initialization Vector) is now generated randomly and written to the stream (and read from the stream on the other end). It's important that the IV is unique for each chunk of data you encrypt for security, and you should also use a random number generator intended for cryptographic purposes like RNGCryptoServiceProvider, rather than a pseudo-random number generator like 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;
}

Finally, we can glue it together:

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

Note that we pass the file stream object to CreateEncryptionStream (and CreateDecryptionStream), and then pass the cryptoStream object to WriteObjectToStream (and ReadObjectfromStream). You'll also notice that the streams are scoped inside using blocks, so that they'll automatically be cleaned up when we're finished with them.

Here's the full test program:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...