【问题标题】:Read a special stream without knowing the number of bytes在不知道字节数的情况下读取特殊流
【发布时间】:2015-06-04 07:59:30
【问题描述】:

在你说使用Jon Skeet's answer之前,我不能。 我是using this stream,一开始很好,但现在,我遇到了一个小问题。我现在处于一个我不知道我期望的数据量的地步,所以我认为我会使用 Jon Skeet 提供的方法,但不幸的是,当所有数据包都被读取后,它会等待 packet = mPackets.Take()

我不知道如何解决这个问题,流做了它应该做的事情(在开始时),但不幸的是,这是一个我不希望它做它应该做的事情的场合......

我应该尝试重写waitingstream 还是有其他readall 方法在我的情况下效果更好?

我尝试在没有更多可用数据包时返回 0(当我希望它读取所有数据而不是等待时)但随后它会抛出一个 IO end of stream exception

在这里我真的很茫然,我知道我已经为自己挖了一个很深的洞......

到目前为止,我还没有找到比从总长度中减去 TLS 标头、GCM IV 和 MAC 更好的解决方案(如下所述)这对于我现在使用的 1 个套件来说很好,但确实不能为未来的密码套件吹嘘。


关于我为什么这样做的一些背景:
我正在使用https://github.com/bcgit/bc-csharp 创建基于 TLS 的加密,但是由于限制,我无法为其分配 TCP 流,我得到的只是来自 TCP 层的字符串,我无法为其提供适当的 TCP 流。

我正在使用mockPskTlsServer and client,我已经这样创建了它:

WaitingStream mStdin = new WaitingStream();
WaitingStream mStdout = new WaitingStream();
CryptoPskTlsClient mServer = new CryptoPskTlsClient(null);
SecureRandom secureRandom = new SecureRandom();
TlsClientProtocol TlsProtocol = new TlsServerProtocol(Stdin, Stdout, secureRandom);
TlsProtocol.Connect(mServer);

这很好用,我还在等待流中添加了一个事件,该事件在流有输出时触发。这样我就可以在创建多个握手消息时获得它们。

这一切都很好,本可以做得更好(更少......被黑客入侵?)但它做了它应该做的事情。

我确实知道让它工作的方法,但这是一个 HACK,我正在寻找一种优雅的方法来做到这一点。这是其中一个 HACKS 的代码示例:

public override string DecryptData(string ciphertext)
{
    byte[] ciphertextBuff = ASCIIEncoding.Default.GetBytes(ciphertext);
    mStdin.Write(ciphertextBuff, 0, ciphertextBuff.Length);

    Stream tlsStream = mServerProtocol.Stream;
    byte[] plaintextBuffer = new byte[ciphertextBuff.Length - 29];//filthy HACK 29 bytes bij AES-GCM want TLS packet = 5, GCM IV = 8, GCM-MAC = 16 totaal = 29.
    Streams.ReadFully(tlsStream, plaintextBuffer);

    string plaintext = ASCIIEncoding.Default.GetString(plaintextBuffer);
    return plaintext;
}

或通过将 x 保留字节中的明文长度添加到 TLS 数据包并在解密之前检索这些字节。

但是从代码示例中可以清楚地看到,readfully 方法中的缓冲区必须是我要提取的字节长度,它可以小于流的长度,但不能更多,因为它会等待无限期。

当我谈到 ReadAllReadFully I mean these methods 之类的方法时

【问题讨论】:

  • 你不能用TryTake代替Take吗?
  • 不确定我是否理解您的问题(最初的问题或这个问题)。为什么要替换初始的 TcpClient Networkstream?
  • @SimonMourier 因为我无法访问 TcpClient 网络流。我只有字符串。
  • @DanielHilgarth 但是当超时超过我该怎么办,我仍然需要返回一些东西,我不能返回 0,因为它会抛出一个异常

标签: c# ssl stream bouncycastle


【解决方案1】:

我得到的只是一个来自 TCP 层的字符串

这无疑是您尝试完成这项工作的致命缺陷。您尝试执行的操作暗示通过 TCP 连接发送的数据是 TLS 加密的。这会产生高度随机的字节值,0 到 255 之间的任何值都是可能的。您从套接字的 Read() 调用中收到的数据包是 byte[],而不是字符串。

将 byte[] 转换为字符串需要使用 .NET Encoding 类。它有很多种,常用的有 ASCIIEncoding、UnicodeEncoding、UTF8Encoding。还有一堆设计用于特定代码页的自定义代码页,您可以使用 Encoding(int codePage) 构造函数获取它们。

我们不知道您需要使用的“TCP 层”使用什么编码,很可能是 UTF8,但是当被要求转换一个字节时,它总是会产生损坏的数据[ ] 带有加密内容。某些字节值没有对应的 Unicode 代码点。编码对象通过生成? 作为替代字符来解决此类问题。无论 Encoding.EncoderFallback 的值是什么。您可以在调试器中看到它们的一些可能性,尽管频率是不可预测的。

换句话说,加密数据将不可避免地被这些替换损坏。对此没有解决方法,您无法恢复原始字节 [],因为替换丢失了内容。这必须由较低层解决,他们要么需要将 byte[] 内容编码为始终可以转换的字符串(base64 是标准解决方案),要么需要停止将数据转换为细绳。你确实需要原始字节 [] 来完成这项工作。

【讨论】:

  • 我知道这一点,它是 ASCII 编码和解码它工作正常,你得到原始数据。这不是问题,请参阅编辑。虽然我意识到这可能不是最简洁的方式,但这不是我面临的问题。
  • 那也行不通,ASCII只支持0-127。拿起电话,与程序员交谈并明确询问他如何将 byte[] 转换为字符串。
  • 我知道他是怎么做到的,我面前有源代码,它是 ASCIIEncod.default.getString(byte[]) 和解码: ASCIIEncod.default.getBytes(string) 这确实有效,我也检查过高于 127 的值。
  • 那是坏代码,它实际上使用了操作系统的默认编码。通常代码页 1252 的名称与您的名称类似。就像我描述的那样破坏数据,1252 没有 0x81、0x8D、0x8F、0x90 和 0x9E 的字符。在程序员销毁数据之前,您必须获取原始字节[],这是非常严格的要求。
  • 我只是耐心地解释为什么你不能完成这项工作。编码是个问题,它也会导致 TLS 解密失败。显然你对我的帮助不感兴趣,所以我能做的就是祝你好运。再见。
【解决方案2】:

您是否尝试在写入后处理输入流?

这应该告诉mStdIn 的内部BlockingCollection<byte> 不会有更多的信息进入。不过,我不确定这是否会传播到您的tlsStream

【讨论】:

  • 问题确实是 TlsStream,如果我想让它工作,我将不得不稍微重写 Tls 代码,但我试图避免重写 Tls 代码的位,但最后,如果没有更好的解决方案。恐怕我不得不做这样的事情。然后虽然我认为我会使用与等待流略有不同的流。
猜你喜欢
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多