2017年2月13日 星期一

[研究][C#]用Email憑證作加密加簽寄信(使用Cpi.Net.SecureMail)(二)ASP.NET

[研究][C#]用Email憑證作加密加簽寄信(使用Cpi.Net.SecureMail)(二)ASP.NET

2017-02-13, 2017-05-25, 2020-08-14,2022-06-06更新

續這篇

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

之前是 C# Console 程式,這篇是 ASP.NET 程式

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

如果是 Windows 2000 Service Pack 3, Windows Server 2003, Windows XP

因IIS預設是使用網路服務(Network Service)去執行,但是網路服務(Network Service)預設是無法讀取本機電腦裡的憑證,可成本機服務(Local Service)方式去執行,不過不建議如此,因本機服務(Local Service)權限太大,所以可能會有安全性的風險,建議只有修改要讀取的憑證給網路服務(Network Service)權限即可,可使用

Windows HTTP Services Certificate Configuration Tool (WinHttpCertCfg.exe)
2012-08-22
支援 Windows 2000 Service Pack 3, Windows Server 2003, Windows XP

用法

,下載安裝後,x86 MS-Windows 預設會安裝在C:\Program Files\Windows Resource Kits\Tools目錄下,x64 MS-Windows 預設安裝在 C:\Program Files (x86)\Windows Resource Kits\Tools,接著到執行CMD指令叫出命令提示列,執行以下指令

cd /d   C:\Program Files\Windows Resource Kits\Tools
winhttpcertcfg -g -c LOCAL_MACHINE\MY -s "MyCertificate" -a "Network Service"
其中"MyCertificate"就是你的憑證名稱(Subject Name,也就是下圖的 "主體" ),而"Network Service"是要授權的帳號。順利執行完畢即能在ASP.NET環境下寄發加密加簽的信件。



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

如果是 Windows Server 2016 

如果你在 Windows Server 2016 上照 Windows 2000 Service Pack 3, Windows Server 2003, Windows XP,會出現錯誤

Error:  Unable to find or obtain a context for requested certificate


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

Email 憑證每次過期更新,都要重新做一次 (以Windows 2016為例說明)
1.「命令提示字元」下執行mmc,啟動「主控台」
2.在「主控台」,點選「檔案」下拉選單,選「新增/移除嵌入式管理單元」選項
3.「新增或移除嵌入式管理單元」畫面,左邊拉到最下方,選「憑證」,按下中間按鈕「新增」,按下「確定」按鈕
4.「憑證嵌入式管理單元」畫面,預設為「我的使用者帳戶」,改選為「電腦帳戶」,按下「下一步」按鈕
5.「選取電腦」畫面,按下「完成」按鈕
6.「新增或移除嵌入式管理單元」畫面,右邊應該會出現「憑證(本機電腦)」,按下「確定」按鈕
7.在「主控台」,依序樹狀展開「主控台根目錄」、「憑證(本機電腦)」、「個人」、「憑證」,在「憑證」上按下滑鼠右鍵,選「所有工作」,選「匯入」
8.「匯入憑證精靈」畫面,按下「下一步」按鈕
9.檔案總類選全部或 *.pfx,匯入憑證,輸入密碼,憑證存放區選「個人」,把憑證匯入。
10.在「主控台」,依序樹狀展開「主控台根目錄」、「憑證(本機電腦)」、「個人」、「憑證」,在視窗右邊的新匯入憑證上按下滑鼠右鍵,選「所有工作」,選「管理私密金鑰」
11.按下「新增」按鈕,
12.「選取使用者或群組」畫面,按下「進階」按鈕
13.「選取使用者或群組」畫面,按下「立即尋找」按鈕,選取下方IIS_IUSRS,按下「確定」按鈕
14.「選取使用者或群組」畫面,按下「確定」按鈕
15.「XXX 的權限」畫面,勾選「完全控制」和「讀取」
16.完成。

********************************************************************************
詳細圖解

Windows Server 2016 上可用 "主控台" (MMC) 來處理 Windows HTTP Services Certificate Configuration Tool (WinHttpCertCfg.exe) 要進行的事情。
















(下圖)注意,如果不是加入 AD 的電腦,可直接按下「進階」按鈕;


(下圖)注意,如果是加入 AD 的電腦,請先按「位置」按鈕,選最上方自己的電腦可直接按下「進階」按鈕,選最上方自己的電腦,否則之後找不到 IIS_IUSRS







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

修改下載得到的 Cpi.Net.SecureMail 方案
修改CryptoHelper.cs 檔案
把 public static X509Certificate2 FindCertificate(string serialNumber) 整個函釋複製一份,修改成下面


public static X509Certificate2 FindCertificate(string serialNumber, StoreLocation storeLocation)
        {
            //X509Store localStore = new X509Store(StoreName.My);
            X509Store localStore = new X509Store(StoreName.My, storeLocation );

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

            try
            {
                X509Certificate2Collection matches = localStore.Certificates.Find(
                    X509FindType.FindBySerialNumber,
                    serialNumber,
                    true);

                if (matches.Count > 0)
                {
                    return matches[0];
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                localStore.Close();
            }

        }


編譯

Cpi.Net.SecureMail_src\Cpi.Net.SecureMail\bin\Debug 目錄下會有 Cpi.Net.SecureMail.dll 檔案

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

用 Visual Studio 新建立一個 CpiNetSecureMailDemo 方案 (WinForm 程式)

把  Cpi.Net.SecureMail.dll 檔案拷貝到 D:\CodeTemp\CpiNetSecureMailDemo\CpiNetSecureMailDemo\bin 目錄,參考加入 Cpi.Net.SecureMail.dll 檔案




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

<!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">
    <div>
        TO<asp:TextBox ID="TextBox_To" runat="server" Width="500px"></asp:TextBox><br />
        CC<asp:TextBox ID="TextBox_CC" runat="server" Width="500px"></asp:TextBox><br />
        BCC<asp:TextBox ID="TextBox_BCC" runat="server" Width="500px"></asp:TextBox><br />
        Subject<asp:TextBox ID="TextBox_Subject" runat="server" Width="500px"></asp:TextBox><br />
        Content<asp:TextBox ID="TextBox_Content" runat="server" Height="100px" TextMode="MultiLine" Width="500px"></asp:TextBox><br />
        附件<asp:FileUpload ID="FileUpload_Attachment" runat="server" /><br /><br />
        <asp:Button ID="Button1" runat="server" Text="送出" OnClick="Button1_Click" />
        <asp:Label ID="Label_Message" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>


下面黃色字體部分要根據自己情況修改
Default.aspx.cs

using System;
using Cpi.Net.SecureMail;
using System.IO;
using System.Security.Cryptography.X509Certificates;

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

        protected void Button1_Click(object sender, EventArgs e)
        {
            try
            {
                var message = new SecureMailMessage();

                //X509Certificate2 signingCert = new X509Certificate2(@"C:\Certs\MyCert.pfx", "Password123456");
                //或
                // 使用電腦中儲存的憑證,而非檔案,安全性較高
                X509Certificate2 signingCert = CryptoHelper.FindCertificate("3029", StoreLocation.LocalMachine);
                var from = new SecureMailAddress("testuser1@mail.mymailserver2.com", "XX公司", signingCert, signingCert);

                message.From = from;
                if (!string.IsNullOrEmpty(TextBox_To.Text.Trim()))
                {
                    foreach (var item in TextBox_To.Text.Trim().Split(',', ';'))
                    {
                        var to = new SecureMailAddress(item);
                        message.To.Add(new SecureMailAddress(item));
                    }
                }
                if (!string.IsNullOrEmpty(TextBox_CC.Text.Trim()))
                {
                    foreach (var item in TextBox_CC.Text.Trim().Split(',', ';'))
                    {
                        message.CC.Add(new SecureMailAddress(item));
                    }
                }
                if (!string.IsNullOrEmpty(TextBox_BCC.Text.Trim()))
                {
                    foreach (var item in TextBox_BCC.Text.Trim().Split(',', ';'))
                    {
                        message.Bcc.Add(new SecureMailAddress(item));
                    }
                }
                message.Bcc.Add(new SecureMailAddress("testuser1@mail.mymailserver2.com"));
                message.Subject = TextBox_Subject.Text;
                message.Body = TextBox_Content.Text;

                if (FileUpload_Attachment.HasFile)
                {
                    string filePath = FileUpload_Attachment.PostedFile.FileName;
                    string filename = Path.GetFileName(filePath);

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

                    MemoryStream destination = new MemoryStream(bytes);
                    message.Attachments.Add(new SecureAttachment(destination, filename));
                }
                message.IsBodyHtml = false;
                message.IsSigned = true;
                message.IsEncrypted = false;

                //System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient();
                System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("smtp.mymailserver2.com", 25);
                client.Send(message);
                Label_Message.Text = "已送出郵件";
                TextBox_Subject.Text = string.Empty;
                TextBox_Content.Text = string.Empty;
            }
            catch (Exception ex)
            {
                Label_Message.Text = ex.Message;
            }
        }
    }
}


即可在 ASP.NET 網頁上寄出加簽 or 加密信件。

PS:好像就算使用外部 SMTP Server,IIS Web Server 本機仍需要有 SMTP Server 才行 (例如:IIS SMTP Server,而且必須開啟 Relay 轉發信件功能才行 ) 。

[研究] IIS SMTP Server 伺服器 安裝 (Windows 2016)
http://shaurong.blogspot.com/2016/10/iis-smtp-server-windows-2016.html

[研究] IIS SMTP Server 伺服器 架設、開放 Relay 轉送 (Windows 2016)
http://shaurong.blogspot.com/2017/05/iis-smtp-server-windows-2016.html



********************************************************************************
2018-06-26
為了避免每次換新憑證要修改內容,可把憑證值放到 Web.Config 中
Web.Config
<configuration>
    <appSettings>
        <add key="CertificateSN" value="abcd6b44b617927ecf4b2f32301545ce" />
    </appSettings>
</configuration>

Default.aspx.cs
X509Certificate2 signingCert = CryptoHelper.FindCertificate("abcd6b44b617927ecf4b2f32301545ce", StoreLocation.LocalMachine);
改為
string CertificateSN = Common.ReadSetting("CertificateSN", "");
X509Certificate2 signingCert = CryptoHelper.FindCertificate(CertificateSN, StoreLocation.LocalMachine);

Common.cs 中增加
public class Common
{
    public static string ReadSetting(string key, string defaultValue)
    {
        try
        {
            object setting = ConfigurationManager.AppSettings[key];
            if (setting != null && setting.ToString().Length == 0)
            {
                setting = null;
            }

            return (setting == null) ? defaultValue : (String)setting;
        }
        catch
        {
            return defaultValue;
        }
    }
}


2020-08-14 補充,CertificateSN 指「序號」,如下圖


沒有留言:

張貼留言