CryptoStream不像预期的那样刷新

时间:2013-06-24 01:25:38

标签: c# .net networking

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

实际发生的事情是什么都没有。我通过在内部接口上观察与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();
        }
    }
}

3 个答案:

答案 0 :(得分:4)

我认为问题在于你正在使用块cypher - 它总是在块中工作,所以在你到达关闭流的最后一个块之前(此时你有一个带有某些描述的填充的较短块)当你有一个部分块时,没有可以写入流。

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

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

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

答案 1 :(得分:1)

您也可以使用

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

希望它有所帮助;)。

答案 2 :(得分:0)

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