客户:来访
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 --> 确认
4:C --> S Client Hello(包含服务器名称:host1.com)
...如何在 C# 中选择certificate1
5:C<-- S Server Hello, Certificate, Server Hello Done
客户来访https://host2.com/
1:C --> S SYN
2:C<-- S SYN,ACK
3:C --> 确认
4:C --> S Client Hello(包含服务器名称:host2.com)
...如何在 C# 中选择certificate2
5: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;
}
虽然 SslStream 本身不支持 SNI,但我已经确认可以解决此问题。 在服务器上,如果在启动 SslStream 之前从 NetworkStream 中读取一些字节,您可以看到从客户端发送到服务器的初始数据包实际上是客户端 hello,其中包含请求的服务器名称。
存在一个问题,因为 NetworkStream 不支持查看字节...因此您必须使用包装流类。 (这里有一个实现:https://stackoverflow.com/a/7281113/1726692)。
还有另一个问题 - 一旦获得字节,您就必须弄清楚如何处理它们。 我确信这是特定于实现的,并且受数十个标准的约束...我目前没有实现解析这些初始字节的东西,但是当我将 Win7 SSlStream 客户端连接到 Win8 SslStream 服务器时,我捕获从客户端到服务器的第一个数据包,我可以非常清楚地看到以这些字节发送到服务器的请求的服务器名称,在服务器上启动 SslStream 之前,可以通过上面的 PeekableStream 提供给服务器。
所以这绝对是可能的。 问题是在哪里可以找到可靠的实现。
无法使用 LocalCertificateSelectionCallback 委托和 SslStream 作为服务器来选择证书。在这种情况下,您只能指定一个证书,作为 AuthenticateAsServer 方法的第一个参数。
MSDN 上SslStream 类的文档还提到了客户端上 LocalCertificateSelectionCallback 委托的用法:
如果服务器需要客户端验证,客户端必须指定 一个或多个用于身份验证的证书。如果客户有更多 不止一个证书,客户可以提供一个 LocalCertificateSelectionCallback 委托来选择正确的 服务器证书。
最后您可以检查这个似乎与您的问题相关的问题SslStream 在充当服务器时是否使用 LocalCertificateSelectionCallback?
我不知道为什么
targetHost
是空白的。
您还可以尝试检查
remoteCertificate.Subject
来识别服务器。
您的 ValidateServerCertificate
方法应确保其与主机匹配。
从 .NET Core 2.1/.NET Standard 2.1 开始,通过可以传递给
SslServerAuthenticationOptions
的 AuthenticateAsServer()
,这是可能的。