2022年6月12日 星期日

[研究][C#][ASP.NET] 加簽寄信(使用 MailKit 和 MimeKit)(三)多收件者與單一附件

[研究][C#][ASP.NET] 加簽寄信(使用 MailKit 和 MimeKit)(三)多收件者與單一附件

2022-06-12

********************************************************************************
相關數篇

[研究][C#][ASP.NET] 加簽寄信(使用 MailKit 和 MimeKit)(三)多收件者與單一附件
https://shaurong.blogspot.com/2022/06/caspnet-mailkit-mimekit_12.html

[研究][ASP.NET]加簽寄信-Windows Server 2019 IIS 10.0 抓 Key Store 中Email憑證所需的權限設定

[研究][C#][ASP.NET] 加簽寄信(使用 MailKit 和 MimeKit)(二)單一收件者、副本、密件
https://shaurong.blogspot.com/2022/06/caspnet-mailkit-mimekit.html

[研究][C#][ASP.NET] 加簽寄信 (使用 MailKit 和 MimeKit)
https://shaurong.blogspot.com/2019/10/caspnet-mailkit-mimekit_13.html

[研究][C#][ASP.NET] 寄信 (使用 MailKit 和 MimeKit)
https://shaurong.blogspot.com/2019/10/caspnet-mailkit-mimekit_11.html

[研究][C#][ASP.NET] 加簽寄信 (使用 System.Net.Mail.MailMessage)
https://shaurong.blogspot.com/2019/10/caspnet-systemnetmailmailmessage.html
更新補充一些資訊,更新到 2021-11-29

[研究][ASP.NET]單一或多個 Email 格式驗證 (使用C#)

[研究]單一或多個 Email 格式驗證 (使用 HTML5)

********************************************************************************

環境:Visual Studio 2022 + ASP.NET + WebForm + Web Application + C#

先設定權限

[研究][ASP.NET]加簽寄信-Windows Server 2019 IIS 10.0 抓 Key Store 中Email憑證所需的權限設定https://shaurong.blogspot.com/2022/06/aspnet-windows-server-2019-iis-100-key.html

NuGet 要安裝 MailKit  ( System.Data.SQLite 則不用),會自動安裝

Portable.BouncyCastle.1.9.0
System.Buffers.4.5.1
System.Numerics.Vectors.4.5.0
System.Runtime.CompilerServices.Unsafe.4.5.3
System.Memory.4.5.4
System.Text.Encoding.CodePages.4.5.1
MimeKit.3.2.0
System.Threading.Tasks.Extensions.4.5.4
MailKit.3.2.0

Web.Config 部分

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<appSettings>
		<add key="EmailCertificateSN" value="郵件憑證序號" />
	</appSettings>
</configuration>

CommonMailKit.cs

using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Cryptography;
using MimeKit.Utils;
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public static class CommonMailKit
    {
        #region == SendCertificatedMailListbyMailKit ==
        public static string SendCertMailListAttachbyMailKit(
            string emailSubject,
            string emailContent,
            string toAddressList,
            string ccAddressList,
            string bccAddressList,
            FileUpload emailAttachment)
        {

            // http://www.mimekit.net/docs/html/Creating-Messages.htm
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("SysName", "SysName@abcdef.com.tw"));
            char[] stringSeparators = new char[] { ',', ';' };

            if (toAddressList != null)
            {
                toAddressList.Replace(" ", "");//移除半形空白
                InternetAddressList toList = new InternetAddressList();
                foreach (var item in toAddressList.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries))
                {
                    // Invalid local-part at offset 0
                    // https://github.com/jstedfast/MailKit/issues/494
                    // toList.Add(new MailboxAddress(item, item));

                    var address = MailboxAddress.Parse(item);
                    //address.Name = name;
                    toList.Add(address);
                }
                message.To.AddRange(toList);
            }
            else
            {
                return "寄信失敗,收件者Email沒有設定。";
            }

            // Cc 可以沒有
            if (!string.IsNullOrEmpty(ccAddressList))
            {
                ccAddressList.Replace(" ", "");//移除半形空白
                InternetAddressList ccList = new InternetAddressList();
                foreach (var item in ccAddressList.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries))
                {
                    var address = MailboxAddress.Parse(item);
                    //address.Name = name;
                    ccList.Add(address);
                }
                message.Cc.AddRange(ccList);
            }

            // Bcc 可以沒有
            if (!string.IsNullOrEmpty(bccAddressList))
            {
                bccAddressList.Replace(" ", "");//移除半形空白
                InternetAddressList bccList = new InternetAddressList();
                foreach (var item in bccAddressList.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries))
                {
                    var address = MailboxAddress.Parse(item);
                    //address.Name = name;
                    bccList.Add(address);
                }
                message.Bcc.AddRange(bccList);
            }

            // 預設回信收件者
            message.ReplyTo.Add(new MailboxAddress("User567", "User567@abcdef.com.tw"));
            //message.Subject = "Digitally Signing Email Test";
            message.Subject = emailSubject;

            //            message.Body = new MimeKit.TextPart("plain")
            //            {
            //                Text = @"Hey Alice,

            //What are you up to this weekend? Monica is throwing one of her parties on
            //Saturday and I was hoping you could make it.

            //Will you be my +1?

            //-- Joey
            //"
            //            };

            // http://www.mimekit.net/docs/html/Creating-Messages.htm
            var builder = new BodyBuilder();

            // Set the plain-text version of the message text
            builder.TextBody = @"Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey
";

            // In order to reference selfie.jpg from the html text, we'll need to add it
            // to builder.LinkedResources and then use its Content-Id value in the img src.
            //var image = builder.LinkedResources.Add(@"C:\Users\Joey\Documents\Selfies\selfie.jpg");
            //image.ContentId = MimeUtils.GenerateMessageId();

            // Set the html version of the message text
//            builder.HtmlBody = string.Format(@"<p>Hey Alice,<br>
//<p>What are you up to this weekend? Monica is throwing one of her parties on
//Saturday and I was hoping you could make it.<br>
//<p>Will you be my +1?<br>
//<p>-- Joey<br>
//<center><img src=""cid:{0}""></center>", image.ContentId);

            builder.TextBody = "EmailBody";

            // We may also want to attach a calendar event for Monica's party...
            // 下面測試可用
            //builder.Attachments.Add(@"C:\Users\Administrator\Desktop\a.png");

            if (emailAttachment != null)
            {
                if (emailAttachment.HasFile)
                {
                    string filePath = emailAttachment.PostedFile.FileName;
                    string filename = Path.GetFileName(filePath);

                    Stream fs = emailAttachment.PostedFile.InputStream;
                    BinaryReader br = new BinaryReader(fs);
                    Byte[] bytes = br.ReadBytes((Int32)fs.Length);

                    MemoryStream destination = new MemoryStream(bytes);
                    builder.Attachments.Add(filename, destination);
                }
            }
            // Now we just need to set the message body and we're done
            message.Body = builder.ToMessageBody();

            //message.Body = new TextPart("plain")
            //{
            //    Text = emailContent
            //};
            

            // http://www.mimekit.net/docs/html/Working-With-SMime.htm
            // Note: by registering our custom context it becomes the default S/MIME context
            // instantiated by MimeKit when methods such as Encrypt(), Decrypt(), Sign(), and
            // Verify() are used without an explicit context.

            //CryptographyContext.Register(typeof(MySecureMimeContext));

            X509Store store = new X509Store("My", StoreLocation.LocalMachine);

            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

            //如果新舊憑證都尚未過期,會抓到舊的憑證
            //X509Certificate2 signCert = store.Certificates.Find(X509FindType.FindBySubjectName, "憑證名稱", false)[0];

            //從Web.Config中抓Email憑證序號值
            string emailCertificateSN = ConfigurationManager.AppSettings["EmailCertificateSN"];
            if (emailCertificateSN == null || emailCertificateSN == "")
            {
                return "讀取不到Email憑證序號。";
            }
            //用 Email憑證序號抓比較不會抓錯
            X509Certificate2 signCert = store.Certificates.Find(X509FindType.FindBySerialNumber, emailCertificateSN, false)[0];

            //用指紋抓
            // X509Certificate2 signCert = store.Certificates.Find(X509FindType.FindByThumbprint, "12339f33449f0cc767feb69e6dc2774ce10c1f60", false)[0];

            // VS 2019 中正常,deploy 後執行,出現錯誤「機碼組不存在」
            // 要用 MMC 設定 Email 憑證可讓 IIS_IUSRS 存取
            CmsRecipient recipient = new CmsRecipient(signCert);

            CmsRecipientCollection colle = new CmsRecipientCollection();
            colle.Add(recipient);

            using (var ctx = new MimeKit.Cryptography.TemporarySecureMimeContext())
            {
                // Note: this assumes that the Sender address has an S/MIME signing certificate
                // and private key with an X.509 Subject Email identifier that matches the
                // sender's email address.
                var ctxsender = message.From.Mailboxes.FirstOrDefault();

                CmsSigner signer = new CmsSigner(signCert);
                message.Body = MultipartSigned.Create(ctx, signer, message.Body);

                // MimeKit.Cryptography.CertificateNotFoundException
                // A valid signing certificate could not be found.
                //message.Body = MultipartSigned.Create(ctx, ctxsender, DigestAlgorithm.Sha1, message.Body);
            }

            // http://www.mimekit.net/docs/html/M_MailKit_Net_Smtp_SmtpClient__ctor.htm
            using (var client = new SmtpClient())
            {
                // IIS SMTP 要設定 None,Auto 會失敗
                //client.Connect("localhost", 25, SecureSocketOptions.None);

                //http://www.mimekit.net/docs/html/T_MailKit_Security_SecureSocketOptions.htm
                //client.Connect("smtp.abcdef.com.tw", 25, false);// 非 SSL連線
                client.Connect("smtp.abcdef.com.tw", 25, SecureSocketOptions.Auto);// Auto SSL連線
                //client.Connect("smtp.abcdef.com.tw", 465, SecureSocketOptions.Auto);
                //client.Connect("smtp.abcdef.com.tw", 587, SecureSocketOptions.Auto);

                // 某些 Mail Server (SMTP) 寄信惠要求帳號、密碼
                // 某些 Mail Server (SMTP) 的帳號是完整含 @ 的Email,有些是 @ 之前的
                client.Authenticate("發信郵件帳號", "發信郵件密碼");
//client.Authenticate("zzzz@gmail.com", "密碼"); try { //foreach (var message in messages) //{ client.Send(message); //} client.Disconnect(true); return ""; //成功 } catch (Exception ex) { if (ex != null) { return ex.Message.ToString(); } else { return "不明錯誤。"; } } } } #endregion } }


Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        Signed Email Test<br />
        <asp:FileUpload ID="FileUpload1" runat="server" /><br />
        <asp:Button ID="Button_SendMail" runat="server" 
            Text="Send" OnClick="Button_SendMail_Click" /><br />
        <asp:Label ID="Label1" runat="server"></asp:Label>
    </form>
</body>
</html>

Default.aspx.cs

using System;

namespace WebApplication1
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
        protected void Button_SendMail_Click(object sender, EventArgs e)
        {
            Label1.Text = "";
            string sendMailResult = CommonMailKit.SendCertMailListAttachbyMailKit
                ("主旨-附件測試", "內容",
                "user123@abcdef.com.tw","","", FileUpload1);
            if (sendMailResult == "")
            {
                Label1.ForeColor = System.Drawing.Color.Green;
                Label1.Text = DateTime.Now.ToString()+ "成功。";
            }
            else
            {
                Label1.ForeColor = System.Drawing.Color.Red;
                Label1.Text = DateTime.Now.ToString() + sendMailResult;
            }
            //----------
            string sendMailResult2 = CommonMailKit.SendCertMailListAttachbyMailKit
                ("主旨-附件測試", "內容",
                ""user123@abcdef.com.tw", "", "", null);
if (sendMailResult2 == "") { Label1.ForeColor = System.Drawing.Color.Green; Label1.Text = DateTime.Now.ToString() + "成功。"; } else { Label1.ForeColor = System.Drawing.Color.Red; Label1.Text = DateTime.Now.ToString() + sendMailResult2; } } } }


(完)

相關

[研究][ASP.NET]加簽寄信-值不能為 null。參數名稱: findValue

mimekit - 在MimeKit上,簽名和加密
http://hant.ask.helplib.com/mimekit/post_4274560

MailKit Documentation - Creating messages
http://www.mimekit.net/docs/html/Creating-Messages.htm
有簡單寄信範例 (但沒有加簽)

MailKit Documentation - Digitally Signing Messages using S/MIME
http://www.mimekit.net/docs/html/Working-With-SMime.htm#Sign

.NET Framework 中過時的類型
https://docs.microsoft.com/zh-tw/dotnet/framework/whats-new/obsolete-types
System.Web.Mail.SmtpMail 過時,建議的替代做法是 System.Net.Mail.SmtpClient。

System.Net.Mail.SmtpClient
https://docs.microsoft.com/zh-tw/dotnet/api/system.net.mail.smtpclient?view=netframework-4.8
System.Net.Mail.SmtpClient 淘汰,建議改用 https://github.com/jstedfast/MailKit 和 https://github.com/jstedfast/MimeKit

GitHub - jstedfast/MailKit: A cross-platform .NET library for IMAP, POP3, and SMTP.
https://github.com/jstedfast/MailKit

GitHub - jstedfast/MimeKit: A .NET MIME creation and parser library with support for S/MIME, PGP, DKIM, TNEF and Unix mbox spools.
https://github.com/jstedfast/MimeKit

[研究][C#][ASP.NET] 加簽寄信 (使用 System.Net.Mail.MailMessage)
https://shaurong.blogspot.com/2019/10/caspnet-systemnetmailmailmessage.html

[研究][C#]加密加簽寄信(使用Cpi.Net.SecureMail)(一)
http://shaurong.blogspot.com/2017/02/ccpinetsecuremail.html

[研究][C#]加密加簽寄信(使用Cpi.Net.SecureMail)(二)
http://shaurong.blogspot.com/2017/02/ccpinetsecuremail_13.html

[研究][C#][ASP.NET] IIS SMTP 寄信失敗,拒絕存取路徑
https://shaurong.blogspot.com/2019/10/caspnet-iis-smtp.html

[研究] [ASP.NET] [C#] [WebForm] 寄信問題
http://shaurong.blogspot.com/2017/06/aspnet-c-webform.html

An S/MIME Library for Sending Signed and Encrypted E-mail
Pete Everett, 15 Jul 2010
https://www.codeproject.com/Articles/41727/An-S-MIME-Library-for-Sending-Signed-and-Encrypted

ASP.NET寄發加密加簽信件
https://www.nccst.nat.gov.tw/ArticlesDetail?lang=zh&seq=1160

Cpi.Net.SecureMail
https://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsecurity%2FCPI_NET_SecureMail%2F%2FCpi.Net.SecureMail_src.zip&zep=Cpi.Net.SecureMail_src%2FCpi.Net.SecureMail%2FSecureMailMessage.cs&obid=41727&obtid=2&ovid=5

如何透過 .NET 送出一個包含 S/MIME 簽章的郵件
2009/06/06 21:20
https://blog.miniasp.com/post/2009/06/06/How-to-send-s-mime-email-using-net

沒有留言:

張貼留言