Serialize, Compress and Encrypt in C# - c#

I want to write a C# class that can serialize, compress and encrypt objects, in that order. I need the resulting file to
Be created as fast as possible
Take as little space as possible
Be as unreadable as possible
I've been researching and coding for a while and this is what I have.
private void SaveObject(string path, object obj)
{
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
string password = "123";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
RijndaelManaged RMCrypto = new RijndaelManaged();
using (CryptoStream cryptoStream = new CryptoStream(fileStream, RMCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write))
using (var gZipStream = new GZipStream(cryptoStream, CompressionMode.Compress))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(gZipStream, obj);
}
}
}
private void LoadObject(string path, out object obj)
{
using (FileStream fileStream = new FileStream(path, FileMode.Open))
{
string password = "123";
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
RijndaelManaged RMCrypto = new RijndaelManaged();
using (CryptoStream cryptoStream = new CryptoStream(fileStream, RMCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read))
using (var gZipStream = new GZipStream(cryptoStream, CompressionMode.Decompress))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
obj = binaryFormatter.Deserialize(gZipStream);
}
}
}
I'm an amateur programmer and I have little knowledge about serialization, streams and encryption. I was even surprised this worked without a problem. My question is: Does this code follow the best programming practice and achieve the goals sufficiently without wasting time or resources?
Note: This is a generic method that I will use in my programs to store data locally.

take a look at https://github.com/HansHinnekint/EncryptionLib. The InfoBlockConvertor code of https://github.com/HansHinnekint/EncryptionLib/blob/master/EncryptionLibrary/InfoBlockConvertor.cs can be used as sample.
Only the compression needs to be added later on. That should not be that difficult.

Related

Unable to apply the new syntax of `using` for StreamWriter

On MSDN about using, there's the new syntax presented. I like it and started to apply it.
Instead of
using (MemoryStream memory = new MemoryStream())
{
using (CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write))
{
using (StreamWriter writer = new StreamWriter(crypto))
{
writer.Write(text);
}
}
}
I can now go
using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using (StreamWriter writer = new StreamWriter(crypto))
writer.Write(text);
However, I can't make the last part work the new way. For some reason, StreamWriter won't allow me to do this.
using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using StreamWriter writer = new StreamWriter(crypto);
writer.Write(text);
I can't really see why. There might be a technical explanation to it in the linked article, as they discuss requirements and limitations. Regrettably, I simply fail to see where they say it and what's implied.
By Skeety's request - full reproducible sample.
public void Test()
{
string text = "hakuna matata";
string code = "1234567890abcdef";
byte[] key = Encoding.ASCII.GetBytes(code);
byte[] iv = key[..16];
using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using StreamWriter writer = new StreamWriter(crypto);
writer.Write(text);
//writer.Flush();
//using (StreamWriter writer = new StreamWriter(crypto))
// writer.Write(text);
string output = Convert.ToBase64String(memory.ToArray());
}
Under your old code, all your resources, including the StreamWriter and CryptoStream, get disposed implicitly at the end of their using block. Upon being disposed, any pending content would get flushed to the MemoryStream. Thus, when you inspect the MemoryStream after the end of these using blocks, it would be correctly populated.
In your new code, the new using syntax means that the resources only get disposed at the end of the enclosing block – in this case, the parent method. Thus, when you inspect the MemoryStream within the same method, you are doing so before StreamWriter and CryptoStream have been disposed, and hence before their contents have been flushed to the MemoryStream.
For the sake of demonstration, calling Dispose on the StreamWriter just before reading the MemoryStream would restore the correct behavior:
using StreamWriter writer = new StreamWriter(crypto);
writer.Write(text);
writer.Dispose()
string output = Convert.ToBase64String(memory.ToArray());
That said, I would recommend against the above, since you're mixing explicit Dispose calls with implicit ones generated by the using.
Alternatively, you could consume the MemoryStream from an outer method, hence ensuring the inner method would have disposed its resources at the end of its execution:
public void Test()
{
var memory = TestInner();
string output = Convert.ToBase64String(memory.ToArray());
}
private MemoryStream TestInner()
{
string text = "hakuna matata";
string code = "1234567890abcdef";
byte[] key = Encoding.ASCII.GetBytes(code);
byte[] iv = key[..16];
using Aes aes = Aes.Create();
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
using MemoryStream memory = new MemoryStream();
using CryptoStream crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write);
using StreamWriter writer = new StreamWriter(crypto);
writer.Write(text);
return memory;
}
However, I would prefer using the old using blocks for clarity in situations like this.

Serialization encryption c# [duplicate]

This question already has answers here:
C# Encrypt serialized file before writing to disk
(4 answers)
Closed 5 years ago.
I use Serialize function to save an object to hard disk by the following code:
using (FileStream fs = new FileStream(fileName, FileMode.Create))
new BinaryFormatter().Serialize(fs, myObject);
Then I reload it again when I need it:
using(FileStream fs = new FileStream(fileName, FileMode.Open))
myObject = (Templates)new BinaryFormatter().Deserialize(fs);
I'm searching an easy way to encrypt the file I save to protect it and also fast way because the time factor in saving and reading the file is very important.
Any suggestions please, thank you in advance!
You're probably looking for something like this:
Aes aes = Aes.Create();
aes.Key = yourByteArrayKey;
aes.IV = yourByteArrayIV;
// Save
using (FileStream fs = new FileStream(fileName, FileMode.Create)) {
using (CryptoStream cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Write)) {
new BinaryFormatter().Serialize(cs, myObject);
}
}
// Load
using (FileStream fs = new FileStream(fileName, FileMode.Open)) {
using (CryptoStream cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Read)) {
myObject = (Templates)new BinaryFormatter().Deserialize(cs);
}
}
You can use any other algorithm as long as it can return an ICrytoTransform, like the aes.CreateEncryptor() method (which is inherited from SymmetricAlgorithm)

How to close a stream when it returns a stream

public Stream DecryptFile(string inputFile)//, string outputFile)
{
{
string password = #"mykey"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cs);
Stream s = sr.BaseStream;
//sr.Close();
//fsCrypt.Close();
return s;
}
}
In this code there is a problem that stream is not closing properly.
If I close it before returning the value then it throws an error.
fsCrypt.Close(); should be performed, but sr.Close(); should not be performed, since the caller of your function should be able to use the Stream.
Also, in order to properly close streams when errors occur, use a disposable context:
using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
{
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cs);
Stream s = sr.BaseStream;
return s;
}
The caller should also use this pattern:
using (var stream = DecryptFile(string inputFile))
{
// do something with decrypted file
}
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
//code here
}
I think it was introduced in ,NET 3.0 or so, and you don´t need to close streams anymore
Everything inside the using brackets will be automatically closed and be disposed of when the code leaves that part
Its propably much better to realize it with usings. Usings close and dispose the underlying stream for you.
public Stream DecryptFile(string inputFile)//, string outputFile)
{
string password = #"mykey"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
using(var fsCrypt = new FileStream(inputFile, FileMode.Open)
{
RijndaelManaged RMCrypto = new RijndaelManaged();
using(CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read))
{
StreamReader sr = new StreamReader(cs);
Stream s = sr.BaseStream;
return s;
}
}
}

.NET AES decryption breaks first few bytes

I'm using AesCryptoServiceProvider to encrypt and decrypt an XML document on disk. There's an example in the MSDN reference that was helpful. I generate the AES key from the SHA-256 hash of a given password. The first half of it is assigned as IV, since I don't know of any better thing to use here. As far as I know, both key and IV must be the same for encrypting and decrypting.
When I decrypt my file, this is what the beginning of it looks like:
I���H璧�-����[�="1.0" encoding="utf-8"?>
The rest of the document is perfectly fine. There's not even some random padding after the content as I would have expected maybe.
What is causing this random garbage at the beginning of the file?
Here's more of the reading code:
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
using (SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider())
{
this.cryptoKey = sha.ComputeHash(Encoding.Unicode.GetBytes(password));
}
aes.Key = this.cryptoKey;
Array.Copy(this.cryptoKey, aes.IV, 16);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (CryptoStream cs = new CryptoStream(fs, decryptor, CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
string data = sr.ReadToEnd();
xdoc.LoadXml(data);
//xdoc.Load(sr);
}
}
And that's the encryption code:
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = this.cryptoKey;
Array.Copy(this.cryptoKey, aes.IV, 16);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (CryptoStream cs = new CryptoStream(fs, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
{
XmlWriter writer = XmlWriter.Create(sw, xws);
xdoc.Save(writer);
writer.Close();
}
}
To begin with, don't generate the key material from an ad-hoc algorithm (and yes, when it comes to key derivation, SHA256 is an ad-hoc algorithm). Follow industry standards and use a trusted Password-Based Key Derivation Function. Current standard is PBKDF-2, see also RFC2898. .Net managed crypto implementation is the Rfc2898DeriveBytes class.
Second, you must show us the encryption code. Looks to me like the sample you used appends the IV used at the beginning of the encrypted stream. Which makes perfect sense, given that the IV should not be derived from the password. The key and IV should be derived from password+random, and the 'random' must be sent as part of the file.
Following Remus Rusanu's advice I've changed my code to use the Rfc2898DeriveBytes class for key generation and write the used salt and IV data to the encrypted file so that I don't need to transport it on a separate channel (like the password).
This is now working for me:
// Setup XML formatting
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;
// Encrypt document to file
byte[] salt = new byte[8];
new RNGCryptoServiceProvider().GetBytes(salt);
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(password, salt);
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (CryptoStream cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
{
fs.Write(salt, 0, salt.Length);
fs.Write(aes.IV, 0, aes.IV.Length);
// Write XmlDocument to the encrypted file
XmlWriter writer = XmlWriter.Create(sw, xws);
xdoc.Save(writer);
writer.Close();
}
}
// Decrypt the file
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
byte[] salt = new byte[8];
fs.Read(salt, 0, salt.Length);
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(this.password, salt);
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
byte[] iv = new byte[aes.BlockSize / 8];
fs.Read(iv, 0, iv.Length);
aes.IV = iv;
using (CryptoStream cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
// Read stream into new XmlDocument
xdoc.Load(sr);
}
}
}
You are correct that the key and IV need to be the same for encrypting and decrypting. In many cases the IV is prepended to the cyphertext and needs to be removed before decryption. The symptom of this is extra garbage before the real start of the file. The garbage is the prepended IV.
Alternatively, you are not using the same IV, in which case the first 16 bytes of the file are garbage, and you get clean plaintext from the 17th byte onwards (AES has 16 byte blocks).
If both are happening, then you will get the first symptom.
What you have appears to be the first. Try using the first 16 bytes of the incoming file as your IV, and the rest as the actual cyphertext.
The first block of the content is mangled because you provided the wrong IV.
The rest of the content is fine because you provided the right Key.

How do I use C# to encrypt another program?

SO, in Visual C#.NET I would like it to somehow be able to taken in a program (through an open file dialog), then somehow take the bytes of that program and encrypt the bytes, to be executed later.
How would I do that? How would I encrypt, then later decrypt, a program using Visual C#.NET?
This answer shows you how to execute a byte array. One caution, this may cause problems with virus scanners because it is common in malware.
If you don't want to execute from memory, I whipped up an example of how you could encrypt store then decrypt and run an executable.
public class FileEncryptRunner
{
Byte[] key = ASCIIEncoding.ASCII.GetBytes("thisisakeyzzzzzz");
Byte[] IV = ASCIIEncoding.ASCII.GetBytes("thisisadeltazzzz");
public void SaveEncryptedFile(string sourceFileName)
{
using (FileStream fStream = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read),
outFStream = new FileStream(Environment.SpecialFolder.MyDocuments+"test.crp", FileMode.Create))
{
Rijndael RijndaelAlg = Rijndael.Create();
using (CryptoStream cStream = new CryptoStream(outFStream, RijndaelAlg.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
StreamWriter sWriter = new StreamWriter(cStream);
fStream.CopyTo(cStream);
}
}
}
public void ExecuteEncrypted()
{
using (FileStream fStream = new FileStream(Environment.SpecialFolder.MyDocuments + "test.crp", FileMode.Open, FileAccess.Read, FileShare.Read),
outFStream = new FileStream(Environment.SpecialFolder.MyDocuments + "crpTemp.exe", FileMode.Create))
{
Rijndael RijndaelAlg = Rijndael.Create();
using (CryptoStream cStream = new CryptoStream(fStream, RijndaelAlg.CreateDecryptor(key, IV), CryptoStreamMode.Read))
{ //Here you have a choice. If you want it to only ever exist decrypted in memory then you have to use the method in
// the linked answer.
//If you want to run it from a file than it's easy and you save the file and run it, this is simple.
cStream.CopyTo(outFStream);
}
}
Process.Start(Environment.SpecialFolder.MyDocuments + "crpTemp.exe");
}
}

Categories

Resources