System.Net.Mail.SmtpClient通過SSL/TLS協議發送郵件失敗問題解決

一、問題描述

1、問題現象

通過System.Net.Mail使用SSL協議發送郵件失敗並報錯 System.Net.Mail.SmtpException: Failure sending mail

詳細報錯信息:

System.Net.Mail.SmtpException: Failure sending mail.   ---> System.IO.IOException: Unable to read data from the transport connection: The connection was closed.     at System.Net.Mail.SmtpReplyReaderFactory.ProcessRead(Byte[] buffer, Int32 offset, Int32 read, Boolean readLine)     at System.Net.Mail.SmtpReplyReaderFactory.ReadLines(SmtpReplyReader caller, Boolean oneLine)     at System.Net.Mail.SmtpReplyReaderFactory.ReadLine(SmtpReplyReader caller)     at System.Net.Mail.SmtpConnection.GetConnection(String host, Int32 port)     at System.Net.Mail.SmtpTransport.GetConnection(String host, Int32 port)     at System.Net.Mail.SmtpClient.GetConnection()     at System.Net.Mail.SmtpClient.Send(MailMessage message)     --- End of inner exception stack trace ---     at System.Net.Mail.SmtpClient.Send(MailMessage message)

2、問題原因

這個問題跟SSL/TLS的協議版本有關係,SSL演化到3.0之後還是不夠安全,因此又出現了SSL的升級版TLS協議,由於建立連接時的區別又分別被稱為顯示SSL和隱式SSL。SSL/TLS協議通常是結對出現SSL/TLS,不過大家還是喜歡簡稱為SSL。

目前最新版本是TLS 1.3,其他可用版本是TLS 1.2和TLS 1.1,其中TLS1.1計劃於2020年棄用

所以,目前主流的郵箱服務商加密協議使用的都是TLS。 但是System.Net.Mail.SmtpClient 不支持較新的TLS協議,具體的TLS協議版本支持情況MSDN上並未找到相關說明

截止到2020年3月受影響的框架版本:

  • .NET Core 2.0-3.1
  • .NET Framework 2.0-4.8

目前微軟MSDN已經將System.Net.Mail.SmtpClient標記為已過期(obsolete),但源碼中並未標記,也並未給出替代實現。

二、解決辦法

1、使用System.Web.Mail

System.Web.Mail.SmtpMail雖然已被標記為已過期,但是畢竟他支持新的SSL/TLS協議。

不過,需要注意的是,System.Web.Mail.SmtpMail,只適用於 .NET Framework(>=2.0)

示例代碼

using System.Web.Mail;  using System;    namespace Ken.IO.Util {        class Program      {          public static void Main (string[] args)          {              MailMessage mmsg = new MailMessage();              mmsg.Subject = "郵件測試主題ken.io";              mmsg.BodyFormat = MailFormat.Html;              mmsg.Body = "郵件測試正文ken.io";              mmsg.BodyEncoding = Encoding.UTF8;              //優先級              mmsg.Priority = MailPriority.High;              //發件者郵箱地址              mmsg.From = "[email protected]";              //收件人收箱地址              mmsg.To = "[email protected]";              mmsg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1");              //用戶名              mmsg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", mmsg.From);              //密碼              mmsg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "password");              //端口              mmsg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpserverport", 465);              //使用SSL              mmsg.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpusessl", "true");              //Smtp服務器              SmtpMail.SmtpServer = "smtp.qq.com";              SmtpMail.Send(mmsg);          }      }  }

2、使用MailKit

MailKit是一個開源的基於MimeKit的跨平台郵件收發類庫,支持IMAP、POP3、SMTP。其中SmtpClient也支持TLS協議.

可以很好的支持 .NET Core以及 .NET Framework框架的郵件發送

安裝Nuget Package

#.NET Core  dotnet add package MailKit --version 2.5.1    #.NET Framework  Install-Package MailKit -Version 2.5.1

示例代碼

using System;  using MailKit.Net.Smtp;  using MailKit;  using MimeKit;    namespace Ken.IO.Util {        class Program      {          public static void Main (string[] args)          {              var message = new MimeMessage ();              message.From.Add (new MailboxAddress ("test", "[email protected]"));              message.To.Add (new MailboxAddress ("test", "[email protected]"));              message.Subject = "郵件測試";              //html or plain              var bodyBuilder = new BodyBuilder ();              bodyBuilder.HtmlBody = "<b>郵件測試html正文ken.io</b>";              bodyBuilder.TextBody = "郵件測試文本正文ken.io";              message.Body = bodyBuilder.ToMessageBody();                using (var client = new SmtpClient ()) {                  client.ServerCertificateValidationCallback = (s,c,h,e) => true;                  //smtp服務器,端口,是否開啟ssl                  client.Connect ("smtp.qq.com", 465, true);                  client.Authenticate ("[email protected]", "password");                  client.Send (message);                  client.Disconnect (true);              }          }      }  }

三、備註

1、附錄