I'm serializing an object via DataContractSerializer without any problems.
But if I now try to serialize this object into a encrypted file I get an exception when deserializing.
Here is my code:
public static bool SerializeDataContract<t>(Stream fileStream, t o, bool bCrypt = false)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(t));
if(bCrypt)
{
TripleDESCryptoServiceProvider crypt = new TripleDESCryptoServiceProvider();
crypt.IV = CRYPT_INIT_VECTOR;
crypt.Key = CRYPT_KEY;
crypt.Padding = PaddingMode.Zeros;
using(CryptoStream cryptoStream = new CryptoStream(fileStream, crypt.CreateEncryptor(), CryptoStreamMode.Write))
{
serializer.WriteObject(cryptoStream, o);
cryptoStream.Close();
}
}
else
serializer.WriteObject(fileStream, o);
return true;
}
public static bool DeserializeDataContract<t>(Stream fileStream, out t o, bool bCrypt = false)
{
o = default(t);
try
{
DataContractSerializer serializer = new DataContractSerializer(typeof(t));
if(bCrypt)
{
TripleDESCryptoServiceProvider crypt = new TripleDESCryptoServiceProvider();
crypt.IV = CRYPT_INIT_VECTOR;
crypt.Key = CRYPT_KEY;
crypt.Padding = PaddingMode.Zeros;
using(CryptoStream cryptoStream = new CryptoStream(fileStream, crypt.CreateDecryptor(), CryptoStreamMode.Read))
{
//TraceXML(cryptoStream);
o = (t)serializer.ReadObject(cryptoStream);
cryptoStream.Close();
}
}
else
{
o = (t)serializer.ReadObject(fileStream);
}
}
catch(Exception ex)
{
return false;
}
return true;
}
If I call the two functions with bCrypt=false, everything works as expected. But if I call the functions with bCrypt=true, I get an exception when deserializing.
Exception is (tranlated from german to english): SerializationException: The data at the root level is invalid.
If I trace the data that is read after decryption, the data seems ok to me, that is it looks just like serialization without encryption.
Do you know, where the bug is in my code?
Or is it just not possible to use encryption with DataContractSerializer?
The problem is that the encrypted data is padded with zeroes so that the length of the original data isn't obvious.
Here's one way to remove them so that deserializing works:
using(var cryptoStream =
new CryptoStream(fileStream, crypt.CreateDecryptor(), CryptoStreamMode.Read))
{
using(var reader = new StreamReader(cryptoStream))
{
var s = reader.ReadToEnd().TrimEnd(new char[]{'\0'});
using(var stream = new MemoryStream(Encoding.ASCII.GetBytes(s)))
{
o = (t)serializer.ReadObject(stream);
}
}
}
if you save to a file the stream after been decripted i think you can easly understand where the problem is
just compare it to the serialized file before encrypting.
If they are equal then you are deserializyng it too early before the decrypter does its job.
I don't think you need to call cryptoStream.Close(); Call Flush() instead
I would use a memoryStream as buffer instead of passing directly CryptoStream to DataContractSerializer and viceversa.
Related
currently im using System.Security.Cryptography and this is my code for it:
private static SymmetricAlgorithm createCryptoServiceProvider(string key, string IV)
{
byte[] password;
using (MD5 md5 = MD5.Create())
password = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
var crypt = new TripleDESCryptoServiceProvider();
byte[] iv = Encoding.UTF8.GetBytes(IV);
crypt.IV = iv;
crypt.Key = password;
return crypt;
}
public static byte[] Serialize(object obj, string key, string key2)
{
var provider = createCryptoServiceProvider(key, key2);
using (MemoryStream memory = new MemoryStream())
{
using (CryptoStream stream = new CryptoStream(memory, provider.CreateEncryptor(), CryptoStreamMode.Write))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
}
return memory.ToArray();
}
}
public static object Deserialize(byte[] inBytes, string key, string key2)
{
var provider = createCryptoServiceProvider(key, key2);
using(MemoryStream memory = new MemoryStream(inBytes))
{
using (CryptoStream stream = new CryptoStream(memory, provider.CreateDecryptor(), CryptoStreamMode.Read))
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
}
}
i use this when getting ready to send data over a socket, I create a object that will have the key in a private field and also keep the key so it knows the key, when the object is received on the other client then it uses a function inside the object that was sent that uses that private string key, key2; and encrypts the message into bytes and sets the keys to "" and then it sends the object back holding the bytes. so now only the original sender can decrypt it. Is this a good way to do this or is there a better way?
Don't try to do the encryption yourself.
http://msdn.microsoft.com/en-us/library/system.net.security.sslstream.aspx
The example code uses TcpClient, but it should work with any stream IO, so direct sockets should be fine also.
I'm not sure what I'm doing wrong, the encryption it seems to work but when you get to the decryption says bad data when trying to deserialize it, not sure what I'm doing wrong. I'm new at doing encryption so if it's something really simple I'm sorry.
public byte[] Serialize(object obj, string key)
{
byte[] returnBytes;
using (MemoryStream memory = new MemoryStream())
{
UTF8Encoding UTF8 = new UTF8Encoding();
TripleDESCryptoServiceProvider crypt = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] pass = provider.ComputeHash(UTF8.GetBytes(key));
crypt.Key = pass;
crypt.Mode = CipherMode.ECB;
crypt.Padding = PaddingMode.PKCS7;
using (CryptoStream stream = new CryptoStream(memory, crypt.CreateEncryptor(), CryptoStreamMode.Write))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
stream.Close();
memory.Close();
}
returnBytes = memory.ToArray();
}
return returnBytes;
}
public object Deserialize(byte[] inBytes, string key)
{
object returnObj;
using (MemoryStream memory = new MemoryStream())
{
UTF8Encoding UTF8 = new UTF8Encoding();
TripleDESCryptoServiceProvider crypt = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] pass = provider.ComputeHash(UTF8.GetBytes(key));
crypt.Key = pass;
crypt.Mode = CipherMode.ECB;
crypt.Padding = PaddingMode.PKCS7;
using (CryptoStream stream = new CryptoStream(memory, crypt.CreateDecryptor(), CryptoStreamMode.Read))
{
BinaryFormatter formatter = new BinaryFormatter();
returnObj = formatter.Deserialize(stream);
stream.Close();
memory.Close();
}
return returnObj;
}
}
This code i did a while back works on strings
public string encrypt(string message, string password)
{
byte[] result;
UTF8Encoding UTF8 = new UTF8Encoding();
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] key = provider.ComputeHash(UTF8.GetBytes(password));
TripleDESCryptoServiceProvider algorithm = new TripleDESCryptoServiceProvider();
algorithm.Key = key;
algorithm.Mode = CipherMode.ECB;
algorithm.Padding = PaddingMode.PKCS7;
byte[] data = UTF8.GetBytes(message);
try
{
ICryptoTransform encryptor = algorithm.CreateEncryptor();
result = encryptor.TransformFinalBlock(data, 0, data.Length);
}
finally
{
algorithm.Clear();
provider.Clear();
}
return Convert.ToBase64String(result);
}
public string decrypt(string message, string passsword)
{
byte[] result;
UTF8Encoding UTF8 = new UTF8Encoding();
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] key = provider.ComputeHash(UTF8.GetBytes(passsword));
TripleDESCryptoServiceProvider algorithm = new TripleDESCryptoServiceProvider();
algorithm.Key = key;
algorithm.Mode = CipherMode.ECB;
algorithm.Padding = PaddingMode.PKCS7;
byte[] data = Convert.FromBase64String(message);
try
{
ICryptoTransform decryptor = algorithm.CreateDecryptor();
result = decryptor.TransformFinalBlock(data, 0, data.Length);
}
finally
{
algorithm.Clear();
provider.Clear();
}
return UTF8.GetString(result);
}
You're not setting the IV property of crypt, so it's starting off as a random value each time. You need to set it to the same value when decrypting as when encrypting - like a salt for hashing. EDIT: Given the way ECB works, it looks like the IV may be ignored, which is why your previous code worked without storing it.
EDIT: While the IV part is certainly required for non-ECB, it's not enough. I'm not sure what the rest of the problem is, although:
The ECB cipher mode isn't recommended - any reason for using it?
You may well end up running into problems due to padding; I don't know if BinaryFormatter handles that for you automatically, but it's worth looking into.
EDIT: Doh - I've worked out the bigger problem; you should indeed be using inBytes, as per Elian's comment. Currently you're completely ignoring the cipher-text - that's got no chance of working!
Here's a complete program showing the whole thing hanging together:
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;
class Test
{
static void Main()
{
byte[] data = Serialize("Some arbitrary test data", "pass");
object x = Deserialize(data, "pass");
Console.WriteLine(x);
}
private static SymmetricAlgorithm CreateCryptoServiceProvider(string key)
{
byte[] passwordHash;
using (MD5 md5 = MD5.Create())
{
// It's not clear why you're taking the hash of the password...
passwordHash = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
}
var crypt = new TripleDESCryptoServiceProvider();
crypt.Key = passwordHash;
crypt.Mode = CipherMode.CBC; // This is the default anyway - can remove
crypt.Padding = PaddingMode.PKCS7; // Ditto
// Fix this to use a randomly generated one and store it for real code.
crypt.IV = new byte[crypt.BlockSize / 8];
return crypt;
}
public static byte[] Serialize(object obj, string key)
{
var provider = CreateCryptoServiceProvider(key);
using (MemoryStream memory = new MemoryStream())
{
using (CryptoStream stream = new CryptoStream(
memory, provider.CreateEncryptor(), CryptoStreamMode.Write))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
}
return memory.ToArray();
}
}
public static object Deserialize(byte[] inBytes, string key)
{
var provider = CreateCryptoServiceProvider(key);
using (MemoryStream memory = new MemoryStream(inBytes))
{
using (CryptoStream stream = new CryptoStream(
memory, provider.CreateDecryptor(), CryptoStreamMode.Read))
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
}
}
}
i have tried following the rules of code analysis on this method:
public static string Encrypt(string password)
{
string myPassword = string.Empty;
if (!string.IsNullOrEmpty(password))
{
myPassword = password;
byte[] Value = System.Text.Encoding.UTF8.GetBytes(myPassword);
SymmetricAlgorithm mCSP = new RijndaelManaged();
mCSP.Key = _key;
mCSP.IV = _initVector;
using (ICryptoTransform ct = mCSP.CreateEncryptor(mCSP.Key, mCSP.IV))
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write))
{
cs.Write(Value, 0, Value.Length);
cs.FlushFinalBlock();
cs.Close();
myPassword = Convert.ToBase64String(ms.ToArray());
}
}
}
}
return myPassword;
}
added all the Try {} Finaly{} blocks, but it was still yelling at me that i dont respect rule 2202.
anyone can give me a hand with this?
yes, i have read other posts about this subject and tried applying it,
but at the end i still get the same message.
To get rid of the CA2202 warning for cs, simply remove the call to its Close method.
The CA2202 problem for ms is a wee bit more complex. The warning is cropping up because CryptoStream has the effrontery to dispose the stream it received via is constructor, which means that there's one inappropriate call to ms.Close() that you can't avoid. The good news is that this untimely disposition has no side-effects in your case, and the same goes for the double disposition, so you can safely slap on a SuppressMessageAttribute and ignore the problem. (For cases where you actually need to passed stream to survive its unpreventable disposition by something like CryptoStream, the usual technique is to use a stream subclass whose disposition can be prevented by its instantiating code.)
Get rid of these two lines, they aren't needed:
cs.FlushFinalBlock();
cs.Close();
Following the documentation on this topic should lead to this code:
public static string Encrypt(string password)
{
string myPassword = string.Empty;
if (!string.IsNullOrEmpty(password))
{
myPassword = password;
byte[] Value = System.Text.Encoding.UTF8.GetBytes(myPassword);
SymmetricAlgorithm mCSP = new RijndaelManaged();
mCSP.Key = _key;
mCSP.IV = _initVector;
using (ICryptoTransform ct = mCSP.CreateEncryptor(mCSP.Key, mCSP.IV))
{
System.IO.MemoryStream ms = null;
try
{
ms = new System.IO.MemoryStream()
var tmp = ms;
using (CryptoStream cs = new CryptoStream(ms, ct,
CryptoStreamMode.Write))
{
ms = null;
cs.Write(Value, 0, Value.Length);
cs.FlushFinalBlock();
cs.Close();
myPassword = Convert.ToBase64String(tmp.ToArray());
}
}
finally
{
if(ms != null)
ms.Dispose();
}
}
}
return myPassword;
}
The documentation on this analysis warning (http://msdn.microsoft.com/en-us/library/ms182334.aspx) gives this example, similar to yours in that it's manipulating streams:
Stream stream = null;
try
{
stream = new FileStream("file.txt", FileMode.OpenOrCreate);
using (StreamWriter writer = new StreamWriter(stream))
{
stream = null;
// Use the writer object...
}
}
finally
{
if(stream != null)
stream.Dispose();
}
but this still gives the error. The following will solve the error:
Stream stream = null;
StreamWriter writer = null;
try
{
stream = new FileStream("file.txt", FileMode.OpenOrCreate);
writer = new StreamWriter(stream))
// Do some stuff on the stream writer..
}
finally
{
if(writer != null)
writer.Dispose();
else if(stream != null)
stream.Dispose();
}
The reason is simple; if the writer will always dispose the stream for you. Only in the scenario the writer is not successfully created should you dispose the stream yourself. But I must admit I like the following syntax a lot more, and if you create a MemoryStream instead of a FileStream the chance of an exception occurring is small and I would prefer suppressing the CA. Please notice that you can stack using statements, so a extra 'nesting level' is often not required.
using (Stream stream = new FileStream("file.txt", FileMode.OpenOrCreate))
using (StreamWriter writer = new StreamWriter(stream))
{
// Use the writer object...
}
I'm getting a bit desperate here. I'm trying to write an encrypted file with a serialized object to disk and later retrieve the file, decrypt it and deserialize the object back.
UPDATE:
I refactored the code to this:
using (Stream innerStream = File.Create(this.GetFullFileNameForUser(securityContext.User, applicationName)))
{
using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateEncryptor(), CryptoStreamMode.Write))
{
// 3. write to the cryptoStream
//BinaryFormatter bf = new BinaryFormatter();
//bf.Serialize(cryptoStream, securityContext);
XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
xs.Serialize(cryptoStream, securityContext);
}
}
using (Stream innerStream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open))
{
using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read))
{
//BinaryFormatter bf = new BinaryFormatter();
//return (SecurityContextDTO)bf.Deserialize(cryptoStream);
XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
//CryptographicException here
return (SecurityContextDTO)xs.Deserialize(cryptoStream);
}
}
Now I'm getting a cryptographic exception on deserialize: Bad Data
ORIGINAL:
I'm doing this:
public void StoreToFile(SecurityContextDTO securityContext, string applicationName)
{
if (securityContext.LoginResult.IsOfflineMode == false)
{
Stream stream = null;
CryptoStream crStream = null;
try
{
TripleDESCryptoServiceProvider cryptic = GetCryptoProvider();
stream = File.Open(this.GetFullFileNameForUser(securityContext.User, applicationName), FileMode.Create);
crStream = new CryptoStream(stream,
cryptic.CreateEncryptor(), CryptoStreamMode.Write);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(crStream, securityContext);
}
catch(Exception)
{
throw;
}
finally
{
if (crStream != null)
crStream.Close();
}
}
}
public SecurityContextDTO RetrieveFromFile(UserDTO user,string applicationName)
{
SecurityContextDTO objectToSerialize;
Stream stream = null;
CryptoStream crStream=null;
try
{
stream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open);
crStream= new CryptoStream(stream,
GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read);
BinaryFormatter bFormatter = new BinaryFormatter();
//Exception here
objectToSerialize = (SecurityContextDTO)bFormatter.Deserialize(crStream);
}
catch (Exception)
{
objectToSerialize = null;
}
finally
{
if (crStream!=null)
crStream.Close();
}
return objectToSerialize;
}
private static TripleDESCryptoServiceProvider GetCryptoProvider()
{
TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
try
{
cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);
Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
cryptic.IV = db.GetBytes(8);
}
catch (Exception)
{
throw;
}
finally
{
cryptic.Dispose();
}
return cryptic;
}
Encrypting and writing works fine, the file appears on the disk and the content is there (encrypted of course). But when I call the retrieve method I always get a SerializationException
Binary stream '30' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization.
When I leave the cryptographic methods out everything works fine.
So,
You realize that in this code
private static TripleDESCryptoServiceProvider GetCryptoProvider()
{
TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
try
{
cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);
Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
cryptic.IV = db.GetBytes(8);
}
catch (Exception)
{
throw;
}
finally
{
cryptic.Dispose(); // <------- Don't do this until you are done decrypting.
}
return cryptic;
}
you will ALWAYS dispose of the provider meaning you are always using a random key and iv
You are close. However, the stream you pass into the creation of CryptoStream is always, always, always the buffer that will hold your end result. It is not the stream that holds the data you want to encrypt or decrypt. I put the emphasis in there because I remember learning this for the first time and I did exactly what you were doing. So here:
// this is for encryption
var memStreamEncryptedData = new MemoryStream();
var encryptStream = new CryptoStream(memStreamEncryptedData,
transform, CryptoStreamMode.Write);
// this is for decryption
var memStreamDecryptedData = new MemoryStream();
var decryptStream = new CryptoStream(memStreamDecryptedData,
transform, CryptoStreamMode.Write);
Notice in both cases, CryptoStream is being initialized with a blank output stream. Your stream does not enter into the picture until later. So, during a write, you will do the following:
encryptStream.Write(dataToBeEncrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamEncryptedData now safely holds your encrypted data
And during the read, you will:
decryptStream.Write(dataToBeDecrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamDecryptedData now safely holds your decrypted data
So, to save you some trouble, here's a nice simple Symmetric method that will perform both encryption and decryption. The only difference between this and yours is that I am working directly on byte arrays, but perhaps that augmentation can be an exercise:
public static byte[] Symmetric(bool encrypt, byte[] plaintext, string ikey)
{
if (plaintext.Length == 0) return plaintext;
// setting up the services can be very expensive, so I'll cache them
// into a static dictionary.
SymmetricSetup setup;
if (!_dictSymmetricSetup.TryGetValue(ikey, out setup))
{
setup = new SymmetricSetup();
setup.des = new DESCryptoServiceProvider { Mode = CipherMode.CBC,
Padding = PaddingMode.Zeros };
setup.hash = Hash(Encoding.ASCII.GetBytes(ikey));
setup.key = setup.hash.ForceLength(8, 0);
setup.IV = Encoding.ASCII.GetBytes("init vec");
setup.des.Key = setup.key;
setup.des.IV = setup.IV;
setup.encrypt = setup.des.CreateEncryptor(setup.des.Key, setup.des.IV);
setup.decrypt = setup.des.CreateDecryptor(setup.des.Key, setup.des.IV);
_dictSymmetricSetup[ikey] = setup;
}
var transform = encrypt ? setup.encrypt : setup.decrypt;
var memStreamEncryptedData = new MemoryStream();
var encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Write);
if (encrypt)
encStream.Write(new[] {(byte) ((8 - (plaintext.Length + 1)%8)%8)}, 0, 1);
encStream.Write(plaintext, 0, plaintext.Length);
encStream.FlushFinalBlock();
encStream.Close();
memStreamEncryptedData.Flush();
var ciphertext = memStreamEncryptedData.ToArray();
byte b;
if (!encrypt)
if (byte.TryParse("" + ciphertext[0], out b))
ciphertext = ciphertext.Skip(1).Take(ciphertext.Length - b - 1).ToArray();
return ciphertext;
}
And to call it, you might do something like this:
static public byte[] DecryptData(this byte[] source, string password) {
return Symmetric(false, source, password);
}
static public byte[] EncryptData(this byte[] source, string password) {
return Symmetric(true, source, password);
}
Again, you'll do something slightly different to work with streams, but hopefully you get the gist. Instead of MemoryStream, it will be whatever stream you need to feed into your serializer.
Some previous posts that can be of use:
How do I encrypt a string in vb.net using RijndaelManaged, and using PKCS5 padding?
Does BinaryFormatter apply any compression?
In later, you can see how I stacked compression with encryption with serialization. And it works.
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:
create cryptostream (empty stream)
write contents to cryptostream (encrypt)
save cryptostream to file
create cryptostream from file contents
read from cryptostream (decrypt)