加密的 NetworkStream 挂在 ReadLine() 上



我正在通过下面的简单示例学习如何加密网络流。如果没有加密,这个例子工作得很好,但现在我已经添加了 CryptoStreams,服务器挂在"var data = reader"上。ReadLine(("在客户端写入消息并刷新后。

    static byte[] Key;
    static byte[] IV;
    static void Main(string[] args)
    {
        var svrTask = RunServer();
        RunClient();
        svrTask.Wait();
    }
    static async Task RunServer ()
    {
        var listener = new TcpListener(4567);
        var algo = new RijndaelManaged();
        listener.Start();
        var client = await listener.AcceptTcpClientAsync();
        using (var stream = client.GetStream())
        using (var inputStream = new CryptoStream(stream, algo.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
        using (var outputStream = new CryptoStream(stream, algo.CreateEncryptor(Key, IV), CryptoStreamMode.Write))
        {
            var reader = new StreamReader(inputStream);
            var writer = new StreamWriter(outputStream);
            var data = reader.ReadLine(); // Task hangs here
            writer.WriteLine("Server Received: " + data);
            writer.Flush();
        }
        client.Close();
        listener.Stop();
    }
    static void RunClient ()
    {
        var algo = new RijndaelManaged();
        Key = algo.Key;
        IV = algo.IV;
        var client = new TcpClient("localhost", 4567);
        using (var stream = client.GetStream())
        using (var inputStream = new CryptoStream(stream, algo.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
        using (var outputStream = new CryptoStream(stream, algo.CreateEncryptor(Key, IV), CryptoStreamMode.Write))
        {
            var writer = new StreamWriter(outputStream);
            Console.WriteLine("Client will send: ");
            var data = Console.ReadLine();
            writer.WriteLine(data);
            writer.Flush();
            var reader = new StreamReader(inputStream);
            var response = reader.ReadLine();
            Console.WriteLine("Client received: " + response);
        }
    }

我很确定我错过了一些非常简单的东西。第一个想法是加密弄乱了新行字符的发送,导致服务器在等待分隔符时挂起,但我无法发现问题。

任何帮助将不胜感激。

在你写了一行之后,很可能没有一个完整的加密数据块准备好发送。你不能直接对此做任何事情。你不能冲洗半个街区。

最好的做法是完全放弃这种方法,并使用现成的解决方案,如 WCF 或 HTTPS。你为什么要弄弄套接字?套接字和加密都非常坚硬。例如,您的加密货币是不安全的,因为攻击者可以在您没有发现的情况下更改消息。他可以翻转位。

下一个最好的办法是在发送消息后关闭连接。我不确定这将如何在这里工作。可能,CryptoStream.Flush有效。

或者,使用计数器模式 (CTR(,使块大小为 1 字节。任何内置内容都不支持此功能。你需要一些图书馆。

正如其他人所提到的,CryptoStream在处理数据之前不会完全刷新数据,因为您使用的是块密码 - 它总是在块中工作,并且在您到达处理流的最后一个块之前不会完全刷新。 注意:简单地关闭或冲洗无济于事。

下面是一个基于您的示例的示例

class Program
{
   static byte[] Key = new byte[] { 0x02, 0x02, 0x01, 0x03, 0x02, 0x32,
      0x51, 0x13, 0x02, 0x02, 0x01, 0x03, 0x02, 0x32, 0x51, 0x13, 
      0x02, 0x02, 0x01, 0x03, 0xA2, 0x33, 0x53, 0xF3, 0xE2, 0xB2,
      0xA1, 0x93, 0x32, 0x52, 0x53, 0x83 };
   static byte[] IV = new byte[] { 0x44, 0x82, 0xF1, 0x03, 0xA2, 0x3D,
      0x51, 0x13, 0x42, 0x02, 0x01, 0x03, 0x02, 0x32, 0x51, 0x13 };
   static SymmetricAlgorithm Algo = new RijndaelManaged();
   static void Main(string[] args)
   {
      Algo.KeySize = 256;
      Algo.Padding = PaddingMode.PKCS7;
      Algo.Key = Key;
      Algo.IV = IV;
      var svrTask = RunServer();
      RunClient();
      svrTask.Wait();
   }
  static async Task RunServer()
  {
     byte[] resp = new byte[2048];
     var listener = new TcpListener(4567);
     listener.Start();
     var client = await listener.AcceptTcpClientAsync();
     using (var stream = client.GetStream())
     {
        // should read while DataAvailable
        var bytes = stream.Read(resp, 0, resp.Length);
        Array.Resize<byte>(ref resp, bytes);
        string resp_str = DecryptArray(resp);
        var data = "Server Received: " + resp_str;
        Console.WriteLine(data);
        byte[] enc_resp = EncryptString(data);
        stream.Write(enc_resp, 0, enc_resp.Length);
     }
     client.Close();
     listener.Stop();
  }
  static void RunClient()
  {
     byte[] resp = new byte[2048];
     using (var client = new TcpClient("localhost", 4567))
     using (var stream = client.GetStream())
     {
        Console.WriteLine("Client will send: ");
        var data = Console.ReadLine();
        byte[] enc_msg = EncryptString(data);
        stream.Write(enc_msg, 0, enc_msg.Length);
        // should read while DataAvailable
        var bytes = stream.Read(resp, 0, resp.Length);
        Array.Resize<byte>(ref resp, bytes);
        string response = DecryptArray(resp);
        Console.WriteLine("Client received: " + response);
     }
  }
  static byte[] GetBytes(string str)
  {
     byte[] bytes = new byte[str.Length * sizeof(char)];
     System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
     return bytes;
  }
  static string GetString(byte[] bytes)
  {
     char[] chars = new char[bytes.Length / sizeof(char)];
     System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
     return new string(chars);
  }
  private static byte[] EncryptString(string stringValue)
  {
     return TransformData(GetBytes(stringValue), true);
  }
  private static string DecryptArray(byte[] arrayValue)
  {
     return GetString(TransformData(arrayValue, false));
  }
  private static byte[] TransformData(byte[] dataToTransform, bool enc)
  {
     byte[] result = new byte[0];
     if (dataToTransform != null && dataToTransform.Length > 0)
     {
        try
        {
           using (var transform = (enc) ? Algo.CreateEncryptor() :
             Algo.CreateDecryptor())
           {
              result = transform.TransformFinalBlock(
                 dataToTransform, 0, dataToTransform.Length);
           }
        }
        catch (Exception) { /* Log this */ }
     }
     return result;
  }
}

我也有同样的问题。就我而言,问题出在CryptoStream.Read()(我知道,因为我只读取 C# 中的数据(。因为它是一个不断增长的流,所以它不知道加密数据的最后一个块。不知何故,它调用ICryptoTransform.TransformFinalBlock()并设置HasFlushedFinalBlock标志,最终结束读取。就我而言,文件正在增长,读者有一种感觉,文件已经结束了欺骗CryptoStream调用ICryptoTransform.TransformFinalBlock()

我通过编写一个CircularMemoryStream来解决这个问题,我可以一次从文件中转储 16 字节(块大小(的解密数据。然后我把CircularMemoryStream插在StreamReader上,拿到线路。成功了。

为了方便起见,我在CircularMemoryStream上编写了一个ChunkCryptoStream包装器,每当流读取器调用Read()时,它都会馈送(16 字节的倍数(解密的循环缓冲区。

也可以使用MemoryStream来做到这一点,但内部缓冲区会不断增长。

     var decryptor = algo.CreateDecryptor(....);
     int BLOCK_SIZE_IN_BYTES = algo.BlockSize / 8;
     int READ_SIZE = BLOCK_SIZE_IN_BYTES*16; // Some multiple of BLOCK_SIZE_IN_BYTES
     MemoryStream decryptedStream = new MemoryStream(READ_SIZE*4); // allocate a buffer
     StreamReader clientReader = new StreamReader(decryptedStream);
     byte[] buff = new byte[READ_SIZE];
     while (true)
     {
        int len = 0;
        while ((len = clientInputStream.Read(buff, 0, READ_SIZE)) > 0)
        {
           int leftover = len % BLOCK_SIZE_IN_BYTES;
           len = len - leftover; // get full blocks
           // if it is big enough then we can decrypt it
           if (len > 0)
           {
              decryptor.TransformBlock(buff, 0, len, buff, 0);
              decryptedStream.Write(buff, 0, len);
              decryptedStream.Seek(-len, SeekOrigin.End);
              Console.WriteLine("Written " + len + " bytes to memory buffer");
              do
              {
                 var line = clientReader.ReadLine();
                 if (null != line)
                 {
                    line = line.Replace('x1', ' '); // replace padding bits (for me padding is 1 bit and so the value is 1, if you use base64 encoding then use the no-padding option)
                    Console.WriteLine(String.Join(" ", line.Select(x => String.Format("{0:X}", Convert.ToInt32(x)))));
                    line = line.Trim();
                    Console.WriteLine(line);
                    Console.WriteLine("end line " + lineno);
                    lineno++;
                 }
              } while ((!content.EndOfStream));
           } //if
           if (leftover > 0)
           {
              clientInputStream.Seek(-leftover, SeekOrigin.Current);
              break;
           }
        }
        var cmd = Console.ReadLine("Want more Line?");
     }

相关内容

  • 没有找到相关文章

最新更新