The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

ASP.NET 使用 X509Certificate2 類別匯入憑證檔時發生錯誤

我最近因為需要在 ASP.NET 網站發信,而且發出的電子郵件必須透過 S/MIME 憑證進行簽章,在本機測試的時候都沒有問題,但是一旦將程式部署到客戶端的主機時就會發生『系統找不到指定的檔案 (The system cannot find the file specified.)』的錯誤,檔案明明就有完整的讀取權限,但錯誤訊息卻是「系統找不到指定的檔案」?我已經被這個問題困擾好幾個星期了。

錯誤的程式碼如下:

X509Certificate2 cert = new X509Certificate2(Server.MapPath("~/App_Data/Test.pfx"), "Password");

而我取得的 Stack Trace 內容如下:

[CryptographicException: The system cannot find the file specified.]
   System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr) +33
   System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx) +0
   System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags) +237
   System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password) +131
   ASP.test_aspx.Page_Load(Object sender, EventArgs e) in g:\WebRoot\Test.aspx:16
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
   System.Web.UI.Control.OnLoad(EventArgs e) +99
   System.Web.UI.Control.LoadRecursive() +50
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627

最後我透過微軟的技術支援中心的協助下找到了 KB948154 這份 KB,也成功的解決無法載入憑證檔的問題,詳細問題發生原因文件裡都有說明我就不再贅述,以下分享解決問題的過程,一共三大步驟:

第一步:將PFX或P12憑證檔匯入本機電腦的憑證儲存區

將憑證匯入到本機電腦的憑證儲存區(Local Computer certificate store)是為了要讓憑證可以在本機電腦共用,這樣才可以讓 ASP.NET 正確載入憑證。

1. 在 [開始] -> [執行] --> 輸入 mmc 執行「主控台」程式

2. 按下 Ctrl + M 準備新增嵌入式管理單元

按下 Ctrl + M 準備新增嵌入式管理單元

3. 接著新增 [憑證] --> [電腦帳戶] --> [本機電腦] --> [完成]

新增 [憑證] --> [電腦帳戶] --> [本機電腦] --> [完成]

4. 然後準備匯入你無法載入的 PFX 憑證檔。[選取檔案] --> [輸入密碼] --> [下一步] --> [完成]

準備匯入你無法載入的 PFX 憑證檔。[選取檔案] --> [輸入密碼] --> [下一步] --> [完成]

5. 匯入成功後,你就會在憑證清單的地方看到你剛剛匯入的憑證,其中的 [發給] 欄位所列的名稱你必須先記下來,後面的步驟會用到。

匯入成功後,你就會在憑證清單的地方看到你剛剛匯入的憑證,其中的 [發給] 欄位所列的名稱你必須先記下來,後面的步驟會用到。

第二步:授權憑證的使用權利給 ASP.NET 執行時的那個身份識別

ASP.NET 執行時的身份識別會定義在 IIS 的應用程式集區裡,預設值是「網路服務」,也就是 "Network Service" 帳戶。

1. 先下載安裝 WinHttpCertCfg.exe 工具 ( Windows HTTP Services Certificate Configuration Tool )

2. 安裝好之後,該工具會置於 C:\Program Files\Windows Resource Kits\Tools 目錄下

3. 以下指令範例的 "MyCertificate" 就是你的憑證名稱(subject name),也就是上面那張圖的 [發給] 中的名稱,而 "Network Service" 就是要授權的帳號。

cd /d C:\Program Files\Windows Resource Kits\Tools
winhttpcertcfg -g -c LOCAL_MACHINE\MY -s "MyCertificate" -a "Network Service" 

第三步:修改原本匯入憑證檔的程式碼

原本的程式碼如下:

X509Certificate2 cert = new X509Certificate2(Server.MapPath("~/App_Data/Test.pfx"), "Password");

因為憑證已經被匯入到本機電腦的憑證儲存區,所以必須修改成以下程式碼:

X509Store store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

System.Security.Cryptography.X509Certificates.X509Certificate2 cert  = 
    store.Certificates.Find(X509FindType.FindBySubjectName, "MyCertificate", false)[0];

以上程式碼唯一要注意的地方就是 "MyCertificate" 這個字串,要改成你的憑證名稱(subject name),也就是上面那張圖的 [發給] 中的名稱。

延伸閱讀

相關連結