问题描述
我发现特定的事件序列会导致 TLS 身份验证失败,并在之前成功时显示错误“客户端和服务器无法通信,因为它们没有通用算法”。这是 SChannel 中的错误吗?
使用 .NET 4.7.2 和 c# 开发。
PC 通过相互 TLS 身份验证相互连接。每台 PC 都支持客户端和服务器连接,并且可能是从一台 PC 连接的服务器,但将客户端连接到不同的 PC。 每台 PC 都有自己唯一的证书 (.pfx),我们将其用于服务器和客户端连接。
例如,PC1 和 PC2 都在与 PC3 通信。 PC1 与 PC3 建立连接。 PC3 使用它的 .pfx 调用 SslStream.AuthenticateAsServer,这成功了。
PC3 然后与 PC2 建立客户端连接并使用相同的 .pfx 调用 SslStream.AuthenticateAsClient。现在失败并出现错误“客户端和服务器无法通信,因为它们没有通用算法”。
有趣的是,如果所有应用程序都重新启动并以相反的顺序进行连接,则行为会发生变化。如果先建立从 PC3 到 PC2 的客户端连接,则连接成功,但随后从 PC1 到 PC3 的服务器连接失败,并出现相同的错误。
一旦连接在一个方向上成功,我们就可以了,后续连接也会成功,但如果我们尝试在另一个方向上使用相同的证书,则除非重新启动应用程序,否则它将失败。
此外,我在 'AuthenticateAs' 方法中指定了 SslProtocols.None。 (推荐用于 .NET 4.7.2 以上)。如果我更改为指定 SslProtocols.Tls12,则两个连接都可以正常工作。
我不知道如何进一步调试。任何想法这里发生了什么? SChannel 是否保持某种内部状态,阻止我对客户端和服务器连接使用相同的证书?
我在下面放了一些示例代码来阐明我的意思。请注意,这只是一个示例,用于展示重现此问题的“服务器”和“客户端”代码。
private static bool ValidateCertificate(object sender,X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
{
// do some proper validation of cert...
return true;
}
private X509Certificate UserCertificateSelectionCallback(object sender,string targetHost,X509CertificateCollection localCertificates,X509Certificate remoteCertificate,string[] acceptableIssuers)
{
if (localCertificates != null && localCertificates.Count > 0)
return localCertificates[0];
return null;
}
public async void Listen()
{
var tcpListener = new TcpListener(IPAddress.Any,Port);
try
{
tcpListener.Start();
Console.WriteLine("Server listening...");
var client = await tcpListener.AcceptTcpClientAsync();
Console.WriteLine("Server authenticating...");
var sslStream = new SslStream(client.GetStream(),false,ValidateCertificate);
var cert = new X509Certificate2("test.pfx","password");
await sslStream.AuthenticateAsServerAsync(cert,true,System.Security.Authentication.SslProtocols.None,false);
Console.WriteLine("Server authenticated.");
// wait 5 secs then drop connection
await Task.Delay(5000);
sslStream.dispose();
cert.Reset();
Console.WriteLine("Server dropped.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
finally
{
tcpListener.Stop();
Console.WriteLine("Server stopped Listening.");
}
}
public async void Connect()
{
try
{
using (var socket = new Socket(AddressFamily.InterNetwork,System.Net.sockets.socketType.Stream,ProtocolType.Tcp))
{
Console.WriteLine("Client connecting...");
await socket.ConnectAsync(Host,Port);
Console.WriteLine("Client authenticating...");
var sslStream = new SslStream(new NetworkStream(socket,true),ValidateCertificate,UserCertificateSelectionCallback,EncryptionPolicy.RequireEncryption);
using (var cert = new X509Certificate2("test.pfx","password"))
{
var certificateCollection = new X509Certificate2Collection(cert);
await sslStream.AuthenticateAsClientAsync(Host,certificateCollection,false);
Console.WriteLine("Client authenticated");
// wait 5 secs then drop connection
await Task.Delay(5000);
}
socket.Close();
Console.WriteLine("Client dropped");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.Message);
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)