CryptoStream does not work as expected

The C # .NET Framework 4.5 code I'm working on should allow me to pass text over an encrypted stream to another program. I created two simple programs to demonstrate my problem. EncryptionTestA is the server and is designed to run first. EncryptionTestB is a client and is designed to run the second. When EncryptionTestB connects, it passes the text "hello world" to another program, passing it through CryptoStream. At least theoretically.

Nothing really happens. I confirmed this by watching data transfer from Wireshark on the internal interface. This code absolutely does not transfer data to this form. The only way I was able to get him to send "hello world" was to close the StreamWriter on the client side. The problem is that it also closes the underlying TCP connection, which I don't want to do.

So my question is: how to clear StreamWriter / CryptoStream without closing the underlying TCP connection?

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Collections; using System.Threading; using System.Security; using System.Security.Cryptography; using System.Net; using System.Net.Sockets; namespace EncryptionTestA { class Program { static void Main(string[] args) { TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1892); listener.Start(); TcpClient client = listener.AcceptTcpClient(); NetworkStream ns = client.GetStream(); Rijndael aes = RijndaelManaged.Create(); byte[] key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; byte[] iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; CryptoStream cs = new CryptoStream(ns, aes.CreateDecryptor(key, iv), CryptoStreamMode.Read); StreamReader sr = new StreamReader(cs); String test = sr.ReadLine(); Console.Read(); sr.Close(); cs.Close(); ns.Close(); client.Close(); listener.Stop(); } } } 

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Collections; using System.Threading; using System.Security; using System.Security.Cryptography; using System.Net; using System.Net.Sockets; namespace EncryptionTestB { class Program { static void Main(string[] args) { TcpClient client = new TcpClient(); client.Connect(IPAddress.Parse("127.0.0.1"), 1892); NetworkStream ns = client.GetStream(); Rijndael aes = RijndaelManaged.Create(); byte[] key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; byte[] iv = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; CryptoStream cs = new CryptoStream(ns, aes.CreateEncryptor(key, iv), CryptoStreamMode.Write); StreamWriter sw = new StreamWriter(cs); sw.WriteLine("hello world"); sw.Flush(); //sw.Close(); Console.Read(); sw.Close(); cs.Close(); ns.Close(); client.Close(); } } } 
+4
source share
3 answers

I believe that the problem is that you use a block cipher - it always works in blocks, so until you get to the final block, where you close the stream (at this point you have a shorter block with some description filling) nothing cannot be written to the stream until you have a partial block.

I strongly suspect that if you try to encrypt something longer than "hello world" - try something at least 16 characters or maybe more - you will find that before closing the stream you will get several blocks, but if you don’t to hit the border of the block with the end of your data, you will still skip some at the end.

It is unclear what your possible use case is: if you are trying to send several messages of a certain description to one stream, I would advise you to develop a scheme in which you encrypt each message separately, and then put all this data on the communication channel - with a length prefix so that You knew how large the encrypted message was on the receiving side.

Please note that on the receiving side, I strongly recommend that you avoid using DataAvailable . Just because there is no data available in the stream right now does not mean that you have reached the end of the message ... why do you need a length prefix.

+4
source

You can also use

 byte[] mess = Encoding.UTF8.GetBytes("hello world"); cs.Write(mess, 0, mess.Length); cs.FlushFinalBlock(); //as said in documentation, this method will write everything you have in final block, even if it isn't full 

Hope this helps;).

+1
source

Based on the mcmonkey4eva suggestion, I implemented a solution using MemoryStreams that seems to work; however, I am not completely satisfied with this solution, since I more or less do not consider the original problem as much as I avoid it.

EncryptionTestA is the application that listens for the connection, and EncryptionTestB is the application connection. Once connected, EncryptionTestB sends a "Hello World!" Message. encrypted using AES for EncryptionTestB.

NOTE. Gluing the Rinjdael class allows some backward compatibility if the target system does not work with a higher version of the .NET Framework.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Collections; using System.Threading; using System.Security; using System.Security.Cryptography; using System.Net; using System.Net.Sockets; namespace EncryptionTestA { class Program { static void Main(string[] args) { TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1892); listener.Start(); TcpClient client = listener.AcceptTcpClient(); NetworkStream ns = client.GetStream(); Rijndael aes = RijndaelManaged.Create(); byte[] key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; byte[] iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; aes.Key = key; aes.IV = iv; byte[] message = Program.ReadMessage(ns, aes); String output = System.Text.Encoding.UTF8.GetString(message); Console.WriteLine(output); Console.Read(); ns.Close(); client.Close(); listener.Stop(); } static byte[] ReadMessage(NetworkStream stream, Rijndael aes) { if (stream.CanRead) { byte[] buffer = new byte[4096]; MemoryStream ms = new MemoryStream(4096); CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write); do { int length = stream.Read(buffer, 0, buffer.Length); cs.Write(buffer, 0, length); } while (stream.DataAvailable); cs.Close(); return ms.ToArray(); } else { return new byte[0]; } } } } 

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Collections; using System.Threading; using System.Security; using System.Security.Cryptography; using System.Net; using System.Net.Sockets; namespace EncryptionTestB { class Program { static void Main(string[] args) { TcpClient client = new TcpClient(); client.Connect(IPAddress.Parse("127.0.0.1"), 1892); NetworkStream ns = client.GetStream(); Rijndael aes = RijndaelManaged.Create(); byte[] key = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; byte[] iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; aes.Key = key; aes.IV = iv; Program.SendMessage("hello world!\n", ns, aes); Console.Read(); ns.Close(); client.Close(); } static void SendMessage(String message, NetworkStream stream, Rijndael aes) { byte[] messageBytes = System.Text.Encoding.UTF8.GetBytes(message); Program.SendMessage(messageBytes, stream, aes); } static void SendMessage(byte[] message, NetworkStream stream, Rijndael aes) { if (stream.CanWrite) { MemoryStream ms = new MemoryStream(4096); CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(message, 0, message.Length); cs.Close(); byte[] cipherText = ms.ToArray(); stream.Write(cipherText, 0, cipherText.Length); } else { Console.WriteLine("Error: Stream is not valid for writing.\n"); } } } } 
0
source

Source: https://habr.com/ru/post/1487789/


All Articles