4

私が取り組んでいる C# .NET Framework 4.5 コードでは、暗号化されたストリームを介してテキストを別のプログラムに転送できるようになっています。私の問題を示すために、2 つの簡単なプログラムを作成しました。EncryptionTestA はサーバーであり、最初に実行することを意図しています。EncryptionTestB はクライアントであり、2 番目に実行することを意図しています。EncryptionTestB が接続すると、CryptoStream を介してテキスト "hello world" を他のプログラムに転送します。少なくとも理論上は。

実際に起こることは何もありません。内部インターフェイスで 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();
        }
    }
}
4

4 に答える 4

5

問題は、ブロックサイファーを使用していることだと思います-それは常にブロックで動作するため、ストリームを閉じる最後のブロックに到達するまで(その時点で、いくつかの説明のパディングを含む短いブロックがあります)、何もできません部分ブロックを取得している間、ストリームに書き込まれます。

「hello world」よりも長いものを暗号化しようとすると、少なくとも 16 文字、またはそれ以上のものを試してみると、ストリームを閉じる前にいくつかのブロックが発生することに気付くでしょう。ブロック境界をデータの最後に配置すると、最後にまだ一部が失われます。

最終的なユースケースが何であるかは明確ではありません。同じストリームで何らかの説明の複数のメッセージを送信しようとしている場合は、各メッセージを個別に暗号化してから、そのすべてのデータを配置するスキームを作成することをお勧めします通信チャネルで - 長さの接頭辞を付けて、暗号化されたメッセージが受信側でどれだけ大きいかがわかります。

受信側では、使用を避けることを強くお勧めしますDataAvailable。現在ストリームで利用可能なデータがないからといって、メッセージの最後に達したという意味ではありません...そのため、長さのプレフィックスが必要です。

于 2013-06-29T06:23:35.783 に答える
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 に答える