SMTP客户端在更新到netcore 3.1后停止工作

问题描述 投票:1回答:1

将我的asp.net核心Web API项目从2.2更新到3.1后,当尝试通过System.Net.Mail.SmtpClient发送电子邮件时,出现以下异常:

- 2020-02-09 14:40:26.8163  15  ( SmtpClient.OnSendCompleted => <>c__DisplayClass78_0.<SendMailAsync>b__0 => SmtpClient.HandleCompletion )  -  Error Failed to send E-Mail. -- ( Exception: System.Net.Mail.SmtpException: Failure sending mail.
 ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
   at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
   at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.Security.SslStream.ThrowIfExceptional()
   at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
   at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
   at System.Net.Mail.SmtpConnection.ConnectAndHandshakeAsyncResult.TlsStreamAuthenticateCallback(IAsyncResult result)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source)
   at System.Net.Mail.SmtpConnection.ConnectAndHandshakeAsyncResult.End(IAsyncResult result)
   at System.Net.Mail.SmtpTransport.EndGetConnection(IAsyncResult result)
   at System.Net.Mail.SmtpClient.ConnectCallback(IAsyncResult result)
   --- End of inner exception stack trace ---

[API的端点是通过appsettings.json中的Kestrel部分配置的

  "Kestrel": {
    "EndPoints": {
      "Http": {
        "Url": "http://myapidomain:80"
      },
      "Https": {
        "Url": "https://www.myapidomain:443",
        "Certificate": {
          "Path": <path to pfx file>,
          "Password": <password>
        }
      }
    }
  },

这是我使用SmtpClient的方式:

using (var client = new SmtpClient(_options.MailServiceHost, _options.MailServicePort))
{
    client.EnableSsl = true;
    client.ClientCertificates.Add(_certificate);
    client.DeliveryMethod = SmtpDeliveryMethod.Network;
    client.UseDefaultCredentials = false;
    client.Credentials = new NetworkCredential(_options.MailAccountName, _options.MailAccountPassword);

    var mail = new MailMessage
    {
        From = new MailAddress(author),
        Body = body,
        Subject = subject,
        Sender = new MailAddress(author),
        SubjectEncoding = Encoding.UTF8,
        BodyEncoding = Encoding.UTF8,
        HeadersEncoding = Encoding.UTF8
    };

    mail.Headers.Add("Content-Type", "content=text/html; charset=\"UTF-8\"");
    mail.To.Add(receiver);
    mail.ReplyToList.Add(author);

    _logger.LogInformation($"Sending mail to: {receiver}");
    await client.SendMailAsync(mail);
    return true;
}

以上代码在我切换回aspnetcore2.2版本时有效,因此我怀疑这是证书本身存在的问题(我对两者都使用完全相同的.pfx文件)。另一个重要提示可能是,当使用Windows计算机上的自签名开发证书在本地调试时,它实际上可以在aspnetcore3.1上运行。在生产中,上述代码失败,我在Ubuntu上运行API。

我的猜测是,在asp.net-core 3.x中处理证书的方式有所改变。或Windows库的行为不同。如果需要,我也可以发布两个Startup.cs文件。

任何建议都可以获取有关实际原因或解决方法的更多详细信息?

更新

正如亚当建议的那样,我已切换到MailKit的SmtpClient实现。该错误仍然存​​在于生产中,但是我设法使用client.ServerCertificateValidationCallback来获取有关该错误的更多详细信息。检查回调的ChainStatus属性时,我得到以下日志打印:

X509ChainStatusFlags: RevocationStatusUnknown
StatusInformation: unable to get certificate CRL

有人可以解释为什么无法获得证书CRL导致我的连接失败吗?我该如何解决?

更新2

而且,当我设置client.CheckCertificateRevocation = false时,出现以下错误:

X509ChainStatusFlags: PartialChain
StatusInformation: unable to get local issuer certificate

更新3

似乎netcore框架找不到所需的ca证书,因为当我在Ubuntu计算机上运行以下命令时,我得到与Update 2完全相同的错误消息:

openssl s_client -connect smtp.gmail.com:465
--> Verify return code: 20 (unable to get local issuer certificate)

但是,当我明确指定ca证书的存储路径验证通过时。

openssl s_client -CApath /etc/ssl/certs/ -connect smtp.gmail.com:465
--> Verify return code: 0 (ok)

所以我猜最后一个问题是:如何获取我的aspnet核心Web API来找到Ubuntu的ca证书存储?

asp.net-core openssl ssl-certificate smtpclient asp.net-core-3.1
1个回答
1
投票

因此,如果查看SmtpClient()的文档,您会发现它现在已标记为过时。实际上我很惊讶您没有遇到编译器错误,但是应该有,但这可能是明天的问题。

Microsoft建议使用MailKit库。链接:Nuget-GitHub

我鼓励您使用Mailkit库而不是使用System.Net.Mail.SmtpClient()重新实现代码。除了不被淘汰之外,MailKit是一个非常易于使用的库,开发人员和贡献者投入了大量时间和精力。

这是使该库过时并推荐MailKit的原因是

SmtpClient及其类型的网络设计不良

有趣的事实:.Net Core 1.0无法访问System.Net.Mail命名空间,并且在之后添加了支持,但是,到.NET Core中System.Net.Mail可用时,MailKit是早期的首选库。 NET核心采用者。

© www.soinside.com 2019 - 2024. All rights reserved.