为什么我的 SMTP TLS v1.3 连接尝试导致 Win32Exception:无法联系本地安全机构

Why does my SMTP TLS v1.3 connection attempt result in Win32Exception: The Local Security Authority cannot be contacted

提问人:Jon Cage 提问时间:8/21/2023 最后编辑:Jon Cage 更新时间:8/22/2023 访问量:166

问:

我有一些诊断代码(.net 框架 v4.8 和 C++/CLI 代码,如果您想知道奇怪的语法),它会通过电子邮件报告错误,并且我一直在努力确保它支持 TLS 1.3:

System::Net::ServicePointManager::SecurityProtocol = System::Net::SecurityProtocolType::Tls13;

System::Net::Mail::SmtpClient^ smtpClient = gcnew System::Net::Mail::SmtpClient(emailServer, port);
smtpClient->UseDefaultCredentials = false;
System::Net::NetworkCredential ^_NetworkCredential = gcnew System::Net::NetworkCredential(loginName, loginPassword);
smtpClient->Credentials = _NetworkCredential;
smtpClient->EnableSsl = true;

smtpClient->Send(mailMessage);

当我连接到gmail SMTP服务器(与基于linux的测试服务器相同)时,我得到以下信息:

Authentication error. Please check that the email certificate and system date / time are correct : System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> System.ComponentModel.Win32Exception: The Local Security Authority cannot be contacted
--- End of inner exception stack trace ---
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.TlsStream.CallProcessAuthentication(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
   at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.Mail.SmtpConnection.Flush()
   at System.Net.Mail.ReadLinesCommand.Send(SmtpConnection conn)
   at System.Net.Mail.EHelloCommand.Send(SmtpConnection conn, String domain)
   at System.Net.Mail.SmtpConnection.GetConnection(ServicePoint servicePoint)
   at System.Net.Mail.SmtpTransport.GetConnection(ServicePoint servicePoint)
   at System.Net.Mail.SmtpClient.GetConnection()
   at System.Net.Mail.SmtpClient.Send(MailMessage message)

--- End of inner exception stack trace ---
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest, Boolean renegotiation)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.TlsStream.CallProcessAuthentication(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
   at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.Mail.SmtpConnection.Flush()
   at System.Net.Mail.ReadLinesCommand.Send(SmtpConnection conn)
   at System.Net.Mail.EHelloCommand.Send(SmtpConnection conn, String domain)
   at System.Net.Mail.SmtpConnection.GetConnection(ServicePoint servicePoint)
   at System.Net.Mail.SmtpTransport.GetConnection(ServicePoint servicePoint)
   at System.Net.Mail.SmtpClient.GetConnection()
   at System.Net.Mail.SmtpClient.Send(MailMessage message)

我必须按照以下指南在我的 Win10 机器上手动启用 TLS 1.3 支持: https://www.asustor.com/en/knowledge/detail/?id=&group_id=1011

是否有可能我错过了一个步骤,或者我的 Windows 计算机有问题,会阻止 TLS 1.3 SMTP 连接?

编辑

澄清一下,在我的 Windows 10 机器上,如果我不进行任何更改,如果我检查它,即使 Tls 1.1 和 Tls 1.2 已设置为启用并且默认情况下在注册表中未禁用,也会报告。我可能在这里遗漏了一个步骤,但在我的开发机器和我尝试部署代码的机器上似乎都是这种情况。System::Net::ServicePointManager::SecurityProtocolSsl3 | Tls

SMTP客户端 NET-4.8 TLS1.3

评论

0赞 Panagiotis Kanavos 8/21/2023
Windows 10 上没有 TLS 1.3,无论旧的非 Microsoft 文章声称什么。您链接到的内容不是 Microsoft 指南。官方文档明确表示 TLS 1.3 仅适用于 Windows 11 和 Windows Server 2022。这些旧文章基于最终作为 Windows 11 发布的 Insider 预览版中的字节。
1赞 Panagiotis Kanavos 8/21/2023
最好的解决方案是删除对 TLS 版本进行硬编码的尝试。在 Windows 11、Windows Server 2022 和 Linux 上,默认使用 TLS 1.3。在 Windows 10 上,将使用 TLS 1.2,这是 Google Mail 支持的。
0赞 Jon Cage 8/21/2023
@PanagiotisKanavos - 我看到了你提到的链接,但它没有明确表明 Win10 22H2(这是我正在使用的)根本不支持它。不过,您可能是对的,因为它没有明确列为支持它。
0赞 Jon Cage 8/21/2023
..以及关于未明确设置协议版本的公平观点;我打算删除它,我只是想检查 TLS 1.3 是否在客户端/服务器上受支持。
0赞 Jon Cage 8/22/2023
我向 Microsoft 提出了一个请求,以澄清要添加到有关 TLS 支持的文档中的支持情况Windows 10, version 22H2

答:

0赞 Jon Cage 8/22/2023 #1

我在研究中发现的一切都指向@Panagiotis在原始问题的评论中指出的内容,这表明我们应该删除硬编码值,但这样做会导致类在我的机器上使用 TLS 1.0。System::Net::Mail::SmtpClient

尽管这与Microsoft的建议背道而驰,但我发现可靠地鼓励类使用 >TLS 1.0 的唯一方法是设置:System::Net::Mail::SmtpClient

System::Net::ServicePointManager::SecurityProtocol = System::Net::SecurityProtocolType::Tls13 | System::Net::SecurityProtocolType::Tls12 | System::Net::SecurityProtocolType::Tls11 | System::Net::SecurityProtocolType::Tls;

...根据我的测试(目前我的 Windows 1.2 机器上的 TLS 10)客户端和服务器支持的内容,它能做到最好。

评论

0赞 Panagiotis Kanavos 8/22/2023
首先不要使用 SmtpClient。该类自己的文档有一个很大的警告,说该类已经过时,不再更新,并建议您改用 MailKit/MimeKit。.NET Framework 4.6 中添加了自动 TLS 选择,但此时 SmtpClient 已被标记为已过时。很有可能它从未更新过以使用新算法。
0赞 Panagiotis Kanavos 8/22/2023
服务器支持什么?您是否正在尝试连接到 GMail?MailKit 支持当前的算法和身份验证方法,包括 OAuth。它甚至包括演示如何使用 OAuth 向 GMail 进行身份验证的示例
0赞 Jon Cage 9/15/2023
这是一些旧代码,因此尝试在这种情况下进行最小的更改。不过,将来避免使用 Smtp 客户端是完全有意义的。
0赞 Panagiotis Kanavos 9/15/2023
这个答案不是最小的改变,而是通过重新启用过时的算法来打开一个安全漏洞。MailKit 的 SmtpClient 特意使用相同的方法,因此唯一需要的更改就是更改库。
0赞 Panagiotis Kanavos 9/15/2023
所有受支持的 .NET Framework 版本(即 4.6.2 及更高版本)都使用最佳可用的 TLS 版本。低于 4.6 的 4.x 版本将使用 TLS 1.1,而不是下降到 TLS 1.0,除非服务器需要它。您必须一直回到 .NET Framework 3.5 才能找到默认的 TLS 1.0。所有 Windows 10 版本都附带 4.6
1赞 Panagiotis Kanavos 8/22/2023 #2

真正的解决方案是使用 SmtpClient。该类自己的文档警告说它已经过时了:

重要

建议不要使用 SmtpClient 类进行新开发,因为 SmtpClient 不支持许多新式协议。请改用 MailKit 或其他库。有关详细信息,请参阅不应在 GitHub 上使用 SmtpClient

提议的替代方案 MailKit 非常受欢迎,在 NuGet 中的下载量超过 80M。默认情况下,它不适用于低于 TLS 1.2 的任何内容。从常见问题解答中:

MailKit 试图跟上最新的安全建议,因此不断从默认配置中删除不再被视为安全的旧 SSL 和 TLS 协议。这通常意味着 MailKit 的 SMTP、POP3 和 IMAP 客户端将无法连接到仍在使用较旧 SSL 和 TLS 协议的服务器。目前默认不支持的SSL和TLS协议有:SSL v2.0、SSL v3.0、TLS v1.0和TLS v1.1。

MailKit 的 SmtpClient 类具有类似于 的 API。NET的SmtpClient,具有许多额外的功能。这意味着它几乎(但不完全)是一个直接的替代品。

public static void SendMessages (IList<MimeMessage> messages)
{
    using (var client = new SmtpClient ()) {
        client.Connect ("smtp.myserver.com", 465, SecureSocketOptions.SslOnConnect);

        client.Authenticate ("username", "password");

        foreach (var message in messages) {
            client.Send (message);
        }

        client.Disconnect (true);
    }
}

也可以投射.NET 的 MailMessage 类到 MailKit :MimeMessage

public static void SendMessages (IList<MailMessage> messages)
{

...
        foreach (var message in messages) {
            var msg=(MimeMessage)message
            client.Send(msg);
        }
...
}

甚至:

var msgs=messages.Cast<MimeMessage>();
foreach(var msg in msgs)
{
...
}

评论

0赞 Jon Cage 9/15/2023
破解建议 - 将接受此作为正确答案,并将查看 MailKit 以取代我们现有的解决方案。