4

The C# .NET Framework 4.5 code I'm working on is supposed to allow me to transfer text across an encrypted stream to another program. I've created two simple programs to demonstrate my problem. EncryptionTestA is the server, and is meant to run first. EncryptionTestB is the client and is meant to run second. Once EncryptionTestB connects, it transfers the text "hello world" to the other program by passing it through a CryptoStream. At least in theory.

What actually happens is nothing. I confirmed this by watching for data transfer with Wireshark on the internal interface. This code transfers absolutely no data in it's present form. The only way I was able to get it to send "hello world" was to close the StreamWriter on the client side. The problem with this is that it also closes the underlying TCP connection, which I don't want to do.

So, my question: how do I flush the 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

4 回答 4

5

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

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

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

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

于 2013-06-29T06:23:35.783 回答
2

你也可以使用

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

希望能帮助到你 ;)。

于 2016-09-18T10:28:42.030 回答
0

Based off of mcmonkey4eva's suggestion, I implemented a solution using MemoryStreams which seems to work; however, I am not completely satisfied with this solution since I more or less don't address the original problem so much as I avoid it.

EncryptionTestA is the application listening for a conneciton, and EncryptionTestB is the application connecting. Once connected EncryptionTestB sends the message "Hello World!" encrypted with AES to EncryptionTestB.

NOTE: Sticking with the Rinjdael class allows for some backwards compatibility, in case the target system is not running a higher version of .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");
            }
        }
    }
}
于 2013-06-29T05:04:05.267 回答
0

DisposeCryptoStream访问底层缓冲区之前(在这种情况下,请确保使用leaveOpen底层流):

public byte[] Encrypt(byte[] data, ICryptoTransform transform)
{
    using MemoryStream mem = new();
    using (CryptoStream cs = new(mem, transform, CryptoStreamMode.Write, leaveOpen: true))
    {
        cs.Write(data);
    }
    return mem.ToArray();
}

对于基于块的密码(您正在使用),这与调用CryptoStream.FlushFinalBlock.

于 2021-10-17T22:26:19.103 回答