Here is my code:
public static void Save<T>(T toSerialize, string fileSpec) {
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.Create(fileSpec)) {
using (CryptoStream cryptoStream = new CryptoStream(stream, des.CreateEncryptor(key, iv), CryptoStreamMode.Write)) {
formatter.Serialize(cryptoStream, toSerialize);
cryptoStream.FlushFinalBlock();
}
}
}
public static T Load<T>(string fileSpec) {
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.OpenRead(fileSpec)) {
using (CryptoStream cryptoStream = new CryptoStream(stream, des.CreateEncryptor(key, iv), CryptoStreamMode.Read)) {
return (T)formatter.Deserialize(cryptoStream);
}
}
}
Key and iv are both static byte arrays with a length of 8 which I'm using for testing purposes. There error is as follows:
Binary stream '178' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization
Any help is much appreciated!
One small typo: your Load method should use des.CreateDecryptor, like this:
public static T Load<T>(string fileSpec)
{
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.OpenRead(fileSpec))
{
using (CryptoStream cryptoStream =
new CryptoStream(stream, des.CreateDecryptor(key, iv),
CryptoStreamMode.Read))
{
return (T)formatter.Deserialize(cryptoStream);
}
}
}
Related
I am trying to encrypt and decrypt a stream (a PDF document at base), but I am having issues with this. When I try to open the document after decryption and download, I get the error Failed to load the PDF document.
Do you know why this might be happening?
Here is the code for encryption:
public EncryptResult EncryptStream(Stream dataStream, bool reuseIV = false)
{
RijndaelManaged crypto = new RijndaelManaged();
crypto.Key = _key;
if (!reuseIV || _iv == null)
{
// make a copy of the current IV
_iv = crypto.IV;
}
else
{
// reuse the previous IV
crypto.IV = _iv;
}
var result = new EncryptResult() { IV = crypto.IV };
using (var encryptor = crypto.CreateEncryptor())
{
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
var byteArrayInput = new byte[dataStream.Length];
dataStream.Read(byteArrayInput, 0, byteArrayInput.Length);
csEncrypt.Write(byteArrayInput, 0, byteArrayInput.Length);
dataStream.Close();
result.Cipher = msEncrypt.ToArray();
msEncrypt.Flush();
msEncrypt.Position = 0;
return result;
}
}
}
}
and decryption:
public Stream DecryptStream(byte[] cipher, byte[] iv)
{
RijndaelManaged crypto = new RijndaelManaged();
crypto.Key = _key;
crypto.IV = iv;
crypto.Padding = PaddingMode.Zeros;
using (var decryptor = crypto.CreateDecryptor())
{
using (MemoryStream msDecrypt = new MemoryStream(cipher))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
var sOutputFilename = new MemoryStream();
var fsDecrypted = new StreamWriter(sOutputFilename);
fsDecrypted.Write(new StreamReader(csDecrypt).ReadToEnd());
sOutputFilename.Position = 0;
return sOutputFilename;
}
}
}
}
Thanks in advance.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
//var byteArrayInput = new byte[dataStream.Length];
//dataStream.Read(byteArrayInput, 0, byteArrayInput.Length);
//csEncrypt.Write(byteArrayInput, 0, byteArrayInput.Length);
dataStream.CopyTo(csEncrypt);
dataStream.Close();
//result.Cipher = msEncrypt.ToArray(); // not here - not flushed yet
//msEncrypt.Flush(); // don't need this
//msEncrypt.Position = 0;
}
result.Cipher = msEncrypt.ToArray();
return result;
}
and in the decryptor, get rid of all the StreamReader/StreamWriter stuff.
A PDF file is compressed, ie binary. But this is after the decryption so it can't be your error.
using (var decryptor = crypto.CreateDecryptor())
{
using (MemoryStream msDecrypt = new MemoryStream(cipher))
{
var outputStream = new MemoryStream();
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.CopyTo(outputStream );
}
outputStream .Position = 0;
return outputStream ;
}
}
One issue is, you are likely encrypting excess bytes at the end of your stream, you need to work out how many bytes are read or use
Stream.CopyTo Method
Reads the bytes from the current stream and writes them to another
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!
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);
}
}
}
I'm not sure whether the .Net Library had changed or just the code is wrong. I can't directly run the code written by softwariness.
Since that, I changed the code based on the answer so that it can be used correctly. Here's an example.
public class CryptoSerialization
{
public static void WriteObjectToStream(Stream outputStream, object obj)
{
if (obj is null) throw new ArgumentNullException("obj can't be null");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(inputStream);
}
public static CryptoStream CreateEncryptionStream(Stream outputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(outputStream, rijndael.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
}
public static CryptoStream CreateDecryptionStream(Stream inputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(inputStream, rijndael.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
}
public static void EncryptObjectToFile(object obj, string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Create);
using (CryptoStream cryptoStream = CreateEncryptionStream(file, Key, IV))
{
WriteObjectToStream(cryptoStream, obj);
}
}
public static object DecryptObjectFromFile(string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Open);
using (CryptoStream cryptoStream = CreateDecryptionStream(file, Key, IV))
{
return ReadObjectFromStream(cryptoStream);
}
}
}
[Serializable]
public class Student
{
public string Name;
public int Age;
}
static async Task Main(string[] args)
{
// the original string "[This is an example key string!]";
// I don't know if the length of the string has to be 32, but when I tried 64, it went wrong.
string cryptoKey = "W1RoaXMgaXMgYW4gZXhhbXBsZSBrZXkgc3RyaW5nIV0=";
byte[] Key = Convert.FromBase64String(cryptoKey);
byte[] IV = new byte[16];
using (RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//same as
//Rijndael rijndael = new RijndaelManaged();
//rijndael.GenerateIV();
//byte[] iv = rijndael.IV;
List<Student> students = new List<Student>() { new Student { Name = "John", Age = 10 }, new Student { Name = "Marry", Age = 15 } };
CryptoSerialization.EncryptObjectToFile(students, Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
List<Student> newStudents = (List<Student>)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
newStudents.ForEach((stu) =>
{
Console.WriteLine(stu.Name + ", " + stu.Age);
});
Console.ReadKey();
}
Here is my code:
public static void Save<T>(T toSerialize, string fileSpec) {
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.Create(fileSpec)) {
using (CryptoStream cryptoStream = new CryptoStream(stream, des.CreateEncryptor(key, iv), CryptoStreamMode.Write)) {
formatter.Serialize(cryptoStream, toSerialize);
cryptoStream.FlushFinalBlock();
}
}
}
public static T Load<T>(string fileSpec) {
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.OpenRead(fileSpec)) {
using (CryptoStream cryptoStream = new CryptoStream(stream, des.CreateEncryptor(key, iv), CryptoStreamMode.Read)) {
return (T)formatter.Deserialize(cryptoStream);
}
}
}
Key and iv are both static byte arrays with a length of 8 which I'm using for testing purposes. There error is as follows:
Binary stream '178' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization
Any help is much appreciated!
One small typo: your Load method should use des.CreateDecryptor, like this:
public static T Load<T>(string fileSpec)
{
BinaryFormatter formatter = new BinaryFormatter();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
using (FileStream stream = File.OpenRead(fileSpec))
{
using (CryptoStream cryptoStream =
new CryptoStream(stream, des.CreateDecryptor(key, iv),
CryptoStreamMode.Read))
{
return (T)formatter.Deserialize(cryptoStream);
}
}
}
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!
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);
}
}
}
I'm not sure whether the .Net Library had changed or just the code is wrong. I can't directly run the code written by softwariness.
Since that, I changed the code based on the answer so that it can be used correctly. Here's an example.
public class CryptoSerialization
{
public static void WriteObjectToStream(Stream outputStream, object obj)
{
if (obj is null) throw new ArgumentNullException("obj can't be null");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(inputStream);
}
public static CryptoStream CreateEncryptionStream(Stream outputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(outputStream, rijndael.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
}
public static CryptoStream CreateDecryptionStream(Stream inputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(inputStream, rijndael.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
}
public static void EncryptObjectToFile(object obj, string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Create);
using (CryptoStream cryptoStream = CreateEncryptionStream(file, Key, IV))
{
WriteObjectToStream(cryptoStream, obj);
}
}
public static object DecryptObjectFromFile(string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Open);
using (CryptoStream cryptoStream = CreateDecryptionStream(file, Key, IV))
{
return ReadObjectFromStream(cryptoStream);
}
}
}
[Serializable]
public class Student
{
public string Name;
public int Age;
}
static async Task Main(string[] args)
{
// the original string "[This is an example key string!]";
// I don't know if the length of the string has to be 32, but when I tried 64, it went wrong.
string cryptoKey = "W1RoaXMgaXMgYW4gZXhhbXBsZSBrZXkgc3RyaW5nIV0=";
byte[] Key = Convert.FromBase64String(cryptoKey);
byte[] IV = new byte[16];
using (RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//same as
//Rijndael rijndael = new RijndaelManaged();
//rijndael.GenerateIV();
//byte[] iv = rijndael.IV;
List<Student> students = new List<Student>() { new Student { Name = "John", Age = 10 }, new Student { Name = "Marry", Age = 15 } };
CryptoSerialization.EncryptObjectToFile(students, Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
List<Student> newStudents = (List<Student>)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
newStudents.ForEach((stu) =>
{
Console.WriteLine(stu.Name + ", " + stu.Age);
});
Console.ReadKey();
}
I have a WPF application contains this class:*
{[Serializable]
public class Parametres
{
private string admin_login;
private string admin_pwd;
private string server;
private string db;
private string user;
private string pwd;}
i serialize an object with this function:
public static void Serialize_Parametres(string filename, Parametres obj)
{
using (FileStream fs = File.Open(filename, FileMode.OpenOrCreate))
{
using (CryptoStream cs = new CryptoStream(fs, key.CreateEncryptor(), CryptoStreamMode.Write))
{
XmlSerializer xmlser = new XmlSerializer(typeof(Parametres));
xmlser.Serialize(cs, obj);
}
}
}
it's works fine and it generates a file .txt , but when i try to deserialize this file and get the object parametres whith this function:
public static Parametres DeSerialize_Parametres(string filename)
{
using (FileStream fs = File.Open(filename, FileMode.Open))
{
using (CryptoStream cs = new CryptoStream(fs, key.CreateDecryptor(), CryptoStreamMode.Read))
{
XmlSerializer xmlser = new XmlSerializer(typeof(Parametres));
return (Parametres)xmlser.Deserialize(cs);
}
}
}
i got this error Length of the data to decrypt is invalid in the line return (Parametres)xmlser.Deserialize(cs);
What is exactly the reason of this error? how can i fix it?
When using this techique to serialize an object you have to do in two parts. The length encrypted stream must be stored as part of the final stream and its up to you to do this. However, you should break this up into a more resuable form.
For example, first serialze the graph you want into a byte stream; then
Encrypte the byte stream; then
Save it to a file.
Below is an example on how to Serialize to a file using AES:
public class ObjectXmlSerializer
{
//---------------------------------------------------------------------
public override Byte[] Serialize(Object obj)
{
using (MemoryStream ms = new MemoryStream())
{
new XmlSerializer(obj.GetType()).Serialize(ms, obj);
return ms.ToArray();
}
}
//---------------------------------------------------------------------
public override T Deserialize<T>(Byte[] bObj)
{
using (MemoryStream ms = new MemoryStream(bObj))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(ms);
}
}
//---------------------------------------------------------------------
public override T Deserialize<T>(Stream iostream)
{
return (T)new XmlSerializer(typeof(T)).Deserialize(iostream);
}
}
// next
public static class CryptoSerivces
{
//---------------------------------------------------------------------
public static Byte[] AesEncrypt(Byte[] src, Byte[] key, Byte[] IV)
{
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
try
{
myRijndael.Mode = CipherMode.CBC;
myRijndael.Key = key;
myRijndael.IV = IV;
myRijndael.Padding = PaddingMode.PKCS7;
using (ICryptoTransform encryptor = myRijndael.CreateEncryptor())
using (MemoryStream msEncrypt = new MemoryStream())
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(src, 0, src.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
}
finally
{
myRijndael.Clear();
}
}
}
//---------------------------------------------------------------------
public static Byte[] AesDecrypt(Byte[] src, Byte[] key, Byte[] IV)
{
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
try
{
myRijndael.Mode = CipherMode.CBC;
myRijndael.Key = key;
myRijndael.IV = IV;
myRijndael.Padding = PaddingMode.PKCS7;
using (ICryptoTransform decryptor = myRijndael.CreateDecryptor())
using (MemoryStream msDecrypt = new MemoryStream())
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
csDecrypt.Write(src, 0, src.Length);
csDecrypt.FlushFinalBlock();
return msDecrypt.ToArray();
}
}
finally
{
myRijndael.Clear();
}
}
}
}
// put all the peices together
void SaveToFile(String fileName, Parametres obj)
{
ObjectXmlSerializer oxs = new ObjectXmlSerializer();
Byte[] bObj = oxs.Serialize(obj);
Byte[] bEncObj = CryptoSerivces.AesEncrypt(bObj, SomeKey, SomeIV);
using (FileStream fs = File.Open(filename, FileMode.OpenOrCreate))
{
fs.Write(bEncObj, 0, bEncObj.Length);
}
}
// I'll leave the reading up to you.