CryptoStream不像预期的那样刷新

我正在研究的C#.NET Framework 4.5代码允许我将文本通过加密流传输到另一个程序。 我创建了两个简单的程序来演示我的问题。 EncryptionTestA是服务器,意味着首先运行。 EncryptionTestB是客户端,意味着运行第二。 一旦EncryptionTestB连接,它将文本“hello world”传递给另一个程序,方法是将其传递给CryptoStream。 至少在理论上。

实际发生的事情都没有。 我通过在内部接口上观察与Wireshark的数据传输来确认这一点。 此代码完全不传输其当前forms的数据。 我能够让它发送“hello world”的唯一方法是关闭客户端的StreamWriter。 这个问题是它还关闭了底层的TCP连接,我不想这样做。

所以,我的问题是:如何在不关闭底层TCP连接的情况下刷新StreamWriter / CryptoStream?

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(); } } } 

我相信问题是你正在使用一个块密码 – 它总是在块中运行,所以在你到达最后一个块关闭流之前(此时你有一个较短的块,带有一些描述的填充)没有什么可以当你有一个部分阻止时写入流。

我强烈怀疑,如果你尝试加密比“你好世界”更长的东西 – 尝试至少16个字符,甚至更多的东西 – 你会发现你在关闭流之前得到了一些块,但除非你碰巧碰到了阻止数据结束的边界,最后你仍会遗漏一些。

目前还不清楚你的最终用例是什么:如果你试图在同一个流上发送一些描述的多条消息,我建议你制定一个方案,你可以分别加密每条消息,然后把所有这些数据放到一起在通信信道上 – 使用长度前缀,以便您知道加密消息在接收端有多大。

请注意,在接收方,我完全建议您避免使用DataAvailable 。 只是因为现在流上没有可用的数据并不意味着你已经到了消息的末尾……这就是你想要长度前缀的原因。

你也可以使用

 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 

希望能帮助到你 ;)。

基于mcmonkey4eva的建议,我使用MemoryStreams实现了一个解决方案似乎有效; 但是,我对这个解决方案并不完全满意,因为我或多或少没有解决原始问题,因为我避免了它。

EncryptionTestA是监听连接的应用程序,EncryptionTestB是连接的应用程序。 连接后,EncryptionTestB发送消息“Hello World!” 使用AES加密到EncryptionTestB。

注意:如果目标系统没有运行更高版本的.NET Framework,坚持使用Rinjdael类可以实现一些向后兼容性。

 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"); } } } }