Unable to apply the new syntax of `using` for StreamWriter - c#

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.

Related

Why do I get an empty result when encrypting a string, even though I flushed the input stream?

I want to encrypt some string through RijndaelManaged and get encrypted result. I want to do it with MemoryStream. But I get the empty string. I get the same problem if I use Rijndael class instead of RijndaelManaged. What I did wrong?
static string EncodeString(string text, byte[] key, byte[] iv) {
RijndaelManaged alg = new RijndaelManaged();
var encryptor = alg.CreateEncryptor(key, iv);
string encodedString = null;
using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs)) {
// encrypt the string
sw.Write(text);
sw.Flush();
cs.Flush();
s.Flush();
Console.WriteLine($"Stream position: {s.Position}"); // Oops... It is 0 still. Why?
// get encrypted string
var sr = new StreamReader(s);
s.Position = 0;
encodedString = sr.ReadToEnd(); // I get empty string here
}
return encodedString;
}
Then I use this method:
RijndaelManaged alg = new RijndaelManaged();
alg.GenerateKey();
alg.GenerateIV();
var encodedString = EncodeString("Hello, Dev!", alg.Key, alg.IV); // I get empty string. Why?
You have two issues here:
Problem 1: You try to read the result before it is ready. You need to close the StreamWriter first:
using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs)) {
// encrypt the string
sw.Write(text);
Console.WriteLine(s.ToArray().Length); // prints 0
sw.Close();
Console.WriteLine(s.ToArray().Length); // prints 16
...
}
But why do I need this? Didn't you see all those Flush statements in my code? Yes, but Rijndael is a block cypher. It can only encrypt a block once it has read the full block (or you have told it that this was the final partial block). Flush allows further data to be written to the stream, so the encryptor cannot be sure that the block is complete.
You can solve this by explicitly telling the crypto stream that you are done sending input. The reference implementation does this by closing the StreamWriter (and, thus the CryptoStream) with a nested using statement. As soon as the CryptoStream is closed, it flushes the final block.
using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cs))
{
// encrypt the string
sw.Write(text);
}
Console.WriteLine(s.ToArray().Length); // prints 16
...
}
Alternatively, as mentioned by Jimi in the comments, you can call FlushFinalBlock explicitly. In addition, you can skip the StreamWriter by explicitly converting your base string to a byte array:
using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
{
cs.Write(Encoding.UTF8.GetBytes(text));
cs.FlushFinalBlock();
Console.WriteLine(s.ToArray().Length); // prints 16
...
}
Or, as mentioned by V.Lorz in the comments, you can just dispose the CryptoStream to call FlushFinalBlock implicitly:
using (var s = new MemoryStream())
{
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
{
cs.Write(Encoding.UTF8.GetBytes(text));
}
Console.WriteLine(s.ToArray().Length); // prints 16
...
}
Problem 2: You tried to read the result as a string. Encryption does not work on strings, it works on byte arrays. Thus, trying to read the result as an UTF-8 string will result in garbage.
Instead, you could, for example, use a Base64 representation of the resulting byte array:
return Convert.ToBase64String(s.ToArray());
Here are working fiddles of your code with all those fixes applied:
With StreamWriter: https://dotnetfiddle.net/8kGI4N
Without StreamWriter: https://dotnetfiddle.net/Nno0DF

C# AES encryption behaving strangely

I am trying to encrypt a string in C# using this piece of code:
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
// Write all data to the stream
swEncrypt.Write(plainText);
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
This is largely based on the sample found here: example.
However, the results are somewhat strange and unpredictable - one time I ran the code and got some string as a result, the other time the resulting string (and the msEncrypt stream) were empty, and one time the application even froze. I don't understand what the problem is.
Edit:
I just ran the code again, on this line:
aesAlg.Key = Key;
something strange happened, I got this error:
Here is how I actually call this method:
public static class Authentication
{
...
private static readonly AesEncryption aes = new AesEncryption();
public static string GenerateToken()
{
AuthenticationData data = new AuthenticationData()
{
// some data
};
string serialized = JsonConvert.SerializeObject(data);
return AesEncryption.AesEncrypt(serialized, aes.Key, aes.IV);
}
}
Maybe some problem with buffers and flushing the streams.
Try to using scope just swEncript before msEncript.ToArray()
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
// Write all data to the stream
swEncrypt.Write(plainText);
}
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
There are two problems with this code.
The SteamWriter isn't closed before the stream is used. There's no call to StreamWriter.Flush either. StreamWriter uses a buffer internally and writes it out to the
stream when it's full.
The StreamWriter doesn't know it's writing to
a MemoryStream, all streams look the same to it. Given how small the
plaintext is in most examples, no flushing will occur before trying
to read the data from the memory stream.
UTF8 supports only a limited set of byte sequences. Good encryption algorithms though generate essentially random sequences. Some of them won't correspond to any valid UTF8 sequence. One of the most common way to encode binary data as text is to use Base64 encoding. That's what most encryption or hashing samples show.
The code should change to something like :
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
swEncrypt.Write(plainText);
//CHANGES HERE
swEncrypt.Flush();
return Convert.ToBase64String(msEncrypt.ToArray());
}
The sample doesn't suffer from this problem because it uses explicit using blocks, so the writer is closed before the memory stream is used :
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
Neither Visual Studio nor Resharper would suggest using a using statement in this case, as the writer is clearly closed in the innermost block.
Don't mix using styles
You can mix using blocks and statements, eg :
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
Given the confusion caused already though, you probably shouldn't. The code isn't that much cleaner and it already caused a problem. Someone else looking at the same code in the future may get confused as well and use a bad refactoring
Maybe the key and IV pass in each time you call the AesEncrypt method were different.
For me it's working fine with the example.
static void Main(string[] args)
{
Aes aesAlg = Aes.Create();
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
Console.ReadKey();
}
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
Console.WriteLine(BitConverter.ToString(msEncrypt.ToArray()));
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
}
}
}

AES Encryption in c#

I try to encrypt a file using the example MSDN https://msdn.microsoft.com/ru-ru/library/system.security.cryptography.aes(v=vs.110).aspx
When I encrypt a .txt file, then everything is fine, but when I try to encrypt other files (.bmp, .pdf ...), then the file is not decrypted.
Where is the error there?
I modified the code to download the file
internal static void EncryptAes(string pathData, string pathEnCrypt)
{
string plainText;
using (StreamReader sr = new StreamReader(pathData))
plainText = sr.ReadToEnd();
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
using (FileStream fstream = new FileStream(pathEnCrypt, FileMode.Create))
fstream.Write(encrypted, 0, encrypted.Length);
}
}
StreamReader is supposed to work with text data in particular encoding. Hence you can't use it for binary data.
If file is not huge, you can read file contents into MemmoryStream and use it latter for AES.
Acting on hex/binary data as if it was a string, will result in loss in data and so you won't be able to recover it fully. To get an/more idea you may want to check out this, it explains what you would like to do for VB.NET

Serialize, Compress and Encrypt in 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.

.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.

Categories

Resources