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 ...
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:
You're using an encryptor when reading. This was probably a typo, but it should be a decryptor.
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);
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With