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

实际发生的事情没什么。我通过在内部接口(interface)上使用 Wireshark 观察数据传输来确认这一点。此代码绝对不会以其现有形式传输任何数据。我能够让它发送“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();
        }
    }
}

最佳答案

我相信问题在于您使用的是块密码 - 它总是在块中工作,所以直到您到达关闭流的最后一个块(此时您有一个较短的块并带有一些描述的填充)什么都不能当你有一个部分块时被写入流。

我强烈怀疑,如果您尝试加密比“hello world”更长的内容 - 尝试至少 16 个字符,甚至更多 - 您会发现在关闭流之前会遇到一些块,但除非您碰巧遇到块边界与数据的末尾,你仍然会在最后遗漏一些。

目前尚不清楚您的最终用例是什么:如果您尝试在同一个流上发送多个具有某种描述的消息,我鼓励您制定一个方案,分别对每条消息进行加密,然后将所有数据放入在通信 channel 上 - 带有长度前缀,以便您知道接收方的加密消息有多大。

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

关于c# - CryptoStream 没有像预期的那样刷新,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17266902/

10-09 16:38