在不知道字节数的情况下读取特殊流

时间:2015-06-04 07:59:31

标签: c# ssl stream bouncycastle

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

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

我是否应该尝试重写waitingstream,或者是否有一种不同的readall方法可以在我的情况下更好用?

我已经尝试在没有可用数据包的情况下返回0(当我希望它读取所有数据包而不是等待时)但是它会抛出IO end of stream exception

我真的很茫然,我知道我为自己挖了一个深洞......

到目前为止,我还没有能够提出一个更好的解决方案,而不是从总长度中减去TLS标题和GCM IV和MAC(如下所述)这适用于我现在使用的1套件,但是对未来的密码套件不太好。

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

我正在使用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数据包并在解密之前检索这些字节。

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

当我在谈论ReadAllReadFully I mean these methods

等方法时

2 个答案:

答案 0 :(得分:5)

  

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

这肯定是你尝试这项工作的致命缺陷。您要做的是隐含在TCP连接上发送的数据是TLS加密的。它产生高度随机的字节值,0到255之间的任何值都是可能的。您从套接字的Read()调用中收到的数据包是byte [],而不是字符串。

将byte []转换为字符串需要使用.NET Encoding类。它有很多种,库存分别是ASCIIEncoding,UnicodeEncoding,UTF8Encoding。还有一些用于处理特定代码页的自定义代码页,您可以使用Encoding(int codePage)构造函数来获取它们。

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

换句话说,加密数据将不可避免地被这些替换破坏。没有解决方法,因为内容因替换而丢失,所以无法恢复原始字节[]。这个必须由较低层解决,它们需要将byte []内容编码为可以始终转换的字符串(base64是标准解决方案),或者需要停止将数据转换为串。你确实需要raw byte []才能使它工作。

答案 1 :(得分:1)

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

这应告诉BlockingCollection<byte>的内部mStdIn,不会再有其他内容。但我不确定这是否会传播到您的tlsStream