После обновления моего asp. net -основного веб-проекта 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 настраиваются через раздел Kestrel в настройках приложения. json
"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 для обоих). Другой важный совет может заключаться в том, что он действительно работает на aspnetcore3.1
при локальной отладке с помощью самозаверяющего сертификата разработки на Windows машине. На производстве, где приведенный выше код не работает, я запускаю API на Ubuntu.
Я предполагаю, что либо что-то изменилось с тем, как обрабатываются сертификаты в 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
Subject: CN=smtp.gmail.com, O=Google LLC, L=Mountain View, S=California, C=US
Issuer: CN=GTS CA 1O1, O=Google Trust Services, C=US
Обновление 3
Похоже, что среда Netcore не может найти необходимые сертификаты CA, потому что я получаю точно такое же сообщение об ошибке, как в Обновлении 2, когда я запускаю следующую команду на компьютере с Ubuntu:
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)
Поэтому я думаю, что последний вопрос: Как мне получить pnet -core веб-API для поиска хранилища CA-сертификатов Ubuntu?