在AuthenticateAsServer之前阅读ClientHello

本文关键字:ClientHello AuthenticateAsServer | 更新日期: 2023-09-27 18:20:52

我正在开发一个需要管理不同SSL服务的服务器应用程序。每个服务都有自己的证书(当然还有不同的CN)。我想使用SNI来提供正确的证书,但在对流进行身份验证之前,我很难阅读ClientHello(SNI扩展所在的位置)。

这就是代码:

byte[] ClientHello = new byte[3000];
Stream TargetStream = TargetTcpClient.GetStream();
//Whatever I write here let me be unable to successfully authenticate 
//If I don't read anything (like in this way), it works perfectly  
int ClientHelloN = 0; //TargetStream.ReadByte();// TargetStream.Read(ClientHello, 0, 3000);
/*SNI Logic to extract Server-name*/
Certificate = new X509Certificate2(/*PATH TO CORRECT CERTIFICATE*/);
SslStream SSLStream = new SslStream(TargetStream);
SSLStream.AuthenticateAsServer(Certificate);

如果我直接使用带前缀的Certificate进行身份验证,则没有问题,但当我在身份验证之前尝试从Stream中读取哪怕只是一个字节时,它会在AuthenticateAsServer方法中永远循环。

在AuthenticateAsServer之前阅读ClientHello

我知道我参加聚会有点晚了,但为什么不看看TcpClient的底层套接字呢?

byte[] peekBuffer = new byte[16];
Socket socket = targetTcpClient.Client;
int bytesAvailable = socket.Receive(peekBuffer, SocketFlags.Peek);
if (bytesAvailable > 0)
{
    if (peekBuffer[0] == 0x16) // ClientHello?
    {
        SslStream secureStream = new SslStream(targetTcpClient.GetStream());
    }
}

一旦您从TargetStream读取数据,它就不见了,您的SslStream将错过握手的开始。

我相信您需要做的是用您自己的Stream子类包装从TargetTcpClient.GetStream()获得的Stream。在您的子类中,您可以从流中读取字节来解析ClientHello,但您应该将所有这些字节保存在缓冲区中。子类应该重写Stream.Read()-当调用重写时,如果缓冲区中有任何内容,则返回该内容,否则将调用转发到已封装的流。该方法类似于这个答案,只是子类StreamReader并且只缓冲单个字符。