3

客户端:访问
1. https://host1.com/
2. https://host2.com/

服务器:有两个证书。
证书1.pfx CN=host1.com 和证书2.pfx CN=host2.com

使用wireshark
客户端访问https://host1.com/
1: C --> S SYN
2: C <-- S SYN,ACK
3: C --> S ACK
4: C --> S Client Hello (Contain Server名称:host1.com)
... 如何在 C#
5 中选择证书 1:C <-- S Server Hello, Certificate, Server Hello Done

客户端访问https://host2.com/
1: C --> S SYN
2: C <-- S SYN,ACK
3: C --> S ACK
4: C --> S Client Hello (包含服务器名称: host2.com)
... 如何在 C#
5 中选择 certificate2: c <-- S Server Hello, Certificate, Server Hello Done

SslStream sslStream = new SslStream(
  clientStream,
  false,
  new RemoteCertificateValidationCallback(ValidateServerCertificate),
  new LocalCertificateSelectionCallback(SelectLocalCertificate)
);

X509Certificate2 certificate = new X509Certificate2("certificates1.pfx");

sslStream.AuthenticateAsServer(certificate , false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true);

private X509Certificate SelectLocalCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
  //In Debug, targetHost is empty string and remoteCertificate=null
  //I can't return right Certificates
  return null;
}
private bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    return true;
}
4

3 回答 3

2

虽然 SslStream 本身不支持 SNI,但我已经确认可以解决此问题。在服务器端,如果你在启动 SslStream 之前从 NetworkStream 中读取一些字节,你可以看到从客户端发送到服务器的初始数据包实际上是客户端的 hello,其中包括请求的服务器名称。

有一个问题,因为 NetworkStream 不支持窥视字节......所以你必须使用包装流类。(这里有一个实现:https ://stackoverflow.com/a/7281113/1726692 )。

还有另一个问题——一旦你得到字节,你必须弄清楚如何处理它们。我确信这是特定于实现的,并受数十个标准的约束......我目前没有实现解析这些初始字节的东西,但是当我将 Win7 SSlStream 客户端连接到 Win8 SslStream 服务器时,我捕获从客户端到服务器的第一个数据包,在启动服务器上的 SslStream 之前,我可以非常清楚地看到以这些字节发送到服务器的请求的服务器名称,通过上述 PeekableStream 提供给服务器。

所以这绝对是可能的。问题是在哪里可以找到可靠的实现。

于 2015-01-03T06:14:54.240 回答
1

无法使用 SslStream 作为服务器的 LocalCertificateSelectionCallback 委托来选择证书。在这种情况下,您只能指定一个证书作为 AuthenticateAsServer 方法的第一个参数。

MSDN 上 SslStream 类的文档还提到了客户端上 LocalCertificateSelectionCallback 委托的使用:

如果服务器需要客户端认证,客户端必须指定一个或多个证书进行认证。如果客户端有多个证书,客户端可以提供一个 LocalCertificateSelectionCallback 委托来为服务器选择正确的证书。

最后,您可以检查这个似乎与您的问题有关的问题SslStream 在充当服务器时是否使用 LocalCertificateSelectionCallback?

于 2013-09-15T00:02:58.343 回答
0

我不确定为什么targetHost是空白的。

您还可以尝试检查remoteCertificate.Subject以识别服务器。您的ValidateServerCertificate方法应确保这与主机匹配。

于 2013-09-12T04:50:22.507 回答