﻿# 如何使用 PowerShell 建立開發測試用途的自簽憑證 (Self-Signed Certificate)

[前篇文章](/post/2018/04/23/Using-MakeCert-to-build-Self-Signed-Certificate)我們學會了用老牌的 [MakeCert](https://msdn.microsoft.com/en-us/library/aa386968.aspx) 建立自簽憑證 (self-signed certificates)，對於大部分數位憑證的需求已經綽綽有餘，確實簡單易用。但是 [MakeCert](https://msdn.microsoft.com/en-us/library/aa386968.aspx) 在使用上有許多限制，例如他沒辦法簡單的「**複製**」(Clone) 現有已經過期的憑證 (每次都要輸入完整的參數重建)、無法設定 **金鑰使用方法** (keyUsage)、無法建立 [**Subject Alternative Name**](https://en.wikipedia.org/wiki/Subject_Alternative_Name) (SAN) 多域名憑證等等。這篇文章我將介紹如何利用 Windows 內建的 [New-SelfSignedCertificate](https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate) Cmdlet 來建立自簽憑證。

文章開始前，我先示範一個使用**自簽憑證**的常見問題。

我們用瀏覽器測試一個 TLS/SSL 網站的時候，即便憑證已經成功加入到 "**My**" 憑證儲存區，網站伺服器也正確綁定伺服器憑證，但是你的瀏覽器就是會出現**憑證無效**的警告 ( [NET::ERR\_CERT\_AUTHORITY\_INVALID](https://support.google.com/chrome/answer/6098869) )，如下圖示：

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/49df56b97961869185e8-image_thumb_252.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/47e46f3802877dbad88f-image_3120.png)

如果你點擊 Google Chrome 的「**瞭解詳情**」就可以得到相對完整的說明。

如果從錯誤訊息來看 "[NET::ERR\_CERT\_AUTHORITY\_INVALID](https://support.google.com/chrome/answer/6098869)"，很明顯這個憑證的問題出在 "**CERT AUTHORITY**" (**CA**) 無效，也就是 **憑證授權單位無效** 的意思。

我們[前篇文章](/post/2018/04/23/Using-MakeCert-to-build-Self-Signed-Certificate)就有提到過，**自簽憑證**是一張由**自己這張憑證**所簽署的**憑證**，但在 PKI 架構下，所有憑證都必須經由「**信任的根憑證**」簽署過的憑證，才是有效憑證。所以「自簽憑證」預設永遠無法成為一張合法有效的憑證。

這時你可能會想，我如果把自簽憑證安裝到「**受信任的根憑證授權單位**」(Trusted Root CA) 不就好了？但是 IIS 下的網站站台在繫結 SSL 憑證的時候，預設只能選擇到註冊到 "**My**" 憑證儲存區下的憑證。畢竟「**受信任的根憑證授權單位**」的用途，主要就是用來做簽署其他憑證的用途，並不是拿來做伺服器驗證之用！

不過這個問題很好解決，你只要先將憑證安裝到 "**My**" 憑證儲存區下，然後**匯出**這張憑證的**公開金鑰** ( 不需要匯出**私密金鑰** )，再將該**公開金**鑰匯入到「**受信任的根憑證授權單位**」即可。我們馬上來試試如何操作：

1\. 我先在本機 IIS 建立一個站台，網站名稱為 **www.fabrikam.com**

2\. 建立一個名為 **www.fabrikam.com** 的憑證

> **MakeCert -n "CN=www.fabrikam.com" -ss My -r -pe -sr localmachine -a SHA256 -len 2048**

   **請注意：**Google Chrome 並不支援憑證使用 **SHA1** 這個**簽章雜湊演算法**。，所以我們要設定為 **SHA256** 才是合法有效的憑證，這部分在 IE 或 Edge 無此問題。

3\. 此時你如果直接去看憑證資訊，就會看到這是一張無效的憑證，而且訊息很清楚的寫著【這個 CA 根憑證不受信任。如果你要啟用信任，請將這個憑證安裝到信任根憑證授權單位存放區。】

[![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/1d3a14bfe8af0eb4c168-image_thumb_253.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/e4e11b97a73632de3d61-image_3121.png)

4\. 我們先將該**憑證**匯出，並直接匯入**受信任的根憑證授權單位**。

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/71dac70041447b3f44af-image_thumb_254.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/e058628447500ff45944-image_3122.png)

你不需要匯出 **私密金鑰** (Private Key)：

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ac257c6e5e0109dfdc2c-image_thumb_255.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/963a7e16f39a22c7a1a6-image_3123.png)

你可以選擇 **DER 編碼二位元 X.509 (.CER)** 這項來當成**公開金鑰**的檔案格式

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/bb13a8aeb45307fefc4f-image_thumb_256.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b5583931c360bf14f8f1-image_3124.png)

然後把金鑰儲存到任意資料夾。

最後匯入剛剛匯出的憑證到**受信任的根憑證授權單位**

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/9ae82cfb11378cf177c6-image_thumb_257.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/1998a1d90836dc56e365-image_3125.png)

此時請你驗證一下原本在「**個人**」( **My** ) 憑證儲存區下的憑證，是不是已經變成一張合法、有效的憑證。

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3015f23cba68a7c766f4-image_thumb_258.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a04004623fe566c1d841-image_3126.png)

**請注意：**確認你的憑證合法有效非常重要，並不是「自簽憑證」就不能享有「合法憑證」的權利。畢竟在瀏覽器下有許多 HTML5 功能，在沒有合法憑證的網站是完全無法運作的！

5\. 接著就來設定 IIS 站台綁定這張憑證，並且開啟瀏覽器測試 HTTPS 連線。

這時你會發現，只有 Internet Explorer 與 Microsoft Edge 瀏覽器可以正常瀏覽網站，但是 Google Chrome 與 Firefox 怎樣都無法正常瀏覽網站！

**先來談談 Firefox 瀏覽器**

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3070f6351436ca20cfc9-image_thumb_259.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/8ba3161ffc21767470cb-image_3127.png)

你點擊「**更多資訊**」會連到 [排除在安全性網站上 "SEC\_ERROR\_UNKNOW\_ISSUER" 的錯誤代碼](https://support.mozilla.org/zh-TW/kb/troubleshoot-SEC_ERROR_UNKNOWN_ISSUER) 文件。

Firefox 真的很特別，在所有我用過的瀏覽器中，只有 Firefox 擁有自己的**憑證管理員**，擁有自己的 CA 憑證清單，所以即便在 Windows 作業系統下，Firefox 也不會去管 **Windows 憑證管理員** 中到底安裝了哪些 CA 根憑證，你必須在這邊手動匯入才行。不過，你建立的**自簽憑證**由於並沒有加上必要的**擴充屬性 (extended attributes)**，所以 Firefox 為了安全考量，我們剛剛建立的憑證是無法匯入的。在我的[前篇文章](/post/2018/04/23/Using-MakeCert-to-build-Self-Signed-Certificate)有提過，你在用 MakeCert 建立憑證時，要加入 **\-cy authority** 參數，才能產生一個「合法」的 CA 憑證。但就算你用建立了一個可以匯入 CA 憑證的版本，Firefox 還是無法識別你的網站為有效憑證的，建議就直接「**新增例外網站**」來解決這個問題即可。

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/6179ec5309a2d03bb2e2-image_thumb_260.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/e55fbcf77ffdb639ceb9-image_3128.png)

**現在來談談 Google Chrome 瀏覽器**

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b350ee416b31babe0bad-image_thumb_261.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/c8d552bcca787aeeb330-image_3129.png)

由於我們現在的憑證是合法有效的，所以這次出現的錯誤訊息不一樣了，這次是 [NET::ERR\_CERT\_COMMON\_NAME\_INVALID](https://support.google.com/chrome/answer/6098869) 錯誤，意思是說 **這張憑證的主體名稱無效** 的意思。但這次要查問題就沒那麼簡單了，我花了好長一段時間才真正理解這個憑證問題的發生原因。

在古老的 2000 年推出了這麼一份 [RFC 2818 - HTTP Over TLS](https://tools.ietf.org/html/rfc2818) 規格文件，明確規範 HTTP 要走 TLS 安全通訊的方法。規格中指出，要驗證一個使用 TLS/SSL 安全加密連線的網站伺服器憑證有兩種方法，第一種是從憑證的**擴充屬性**中取得 **[subjectAlternativeName](https://en.wikipedia.org/wiki/Subject_Alternative_Name)** (**subjectAltName**) (**SAN**) 屬性的設定；如果沒有這個擴充屬性的話，就會直接去找憑證中一定會有的 **commonName** (**CN**) 屬性，這裡的 **CN** 屬性就是所謂的 **主體** 名稱 (如下圖示)。

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/f4dfc0b8cb943525b32c-image_thumb_262.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/44f626881337ce870c64-image_3130.png)

不過在 [RFC 2818](https://tools.ietf.org/html/rfc2818) 也提到了，透過 **commonName** (**CN**) 的驗證方式已經 **棄用** (deprecated)。沒錯，在 18 年前的規格中就說好不要再用了，所以 Google Chrome 決定[從 Chrome 58 這個版本開始](https://chromestatus.com/feature/4981025180483584)正式取消 **commonName** (**CN**) 的驗證方式，也因此我們用 MakeCert 所建立的自簽憑證因為無法加入 **subjectAltName** (**SAN**) 擴充屬性，所以永遠於法建立出一張可以給 Google Chrome 瀏覽器合法使用的伺服器憑證！

**鋪陳了這麼久，終於要進入本篇文章的重點了！XD**

我們可以利用 [New-SelfSignedCertificate](https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate) Cmdlet 來建立一個含有 **subjectAltName** (**SAN**) 擴充屬性的自簽憑證。例如我們直接從官網文件中看到的第一個範例，就非常簡單易用，直接可以產生一個含有 SAN 擴充屬性的憑證，並自動註冊到 "**本機電腦**" 的 "**My**" 憑證儲存區中。另一方面，我個人認為透過 PowerShell 來建立**自簽憑證**，最大的好處就是命令很清楚，不像 MakeCert 的參數那樣艱澀難懂。

現在，我們就重新再走一次憑證建立與設定的過程：

1\. 先移除先前建立的 **www.fabrikam.com** 憑證，包含**受信任的根憑證授權單位**下的憑證。

2\. 執行以下 PowerShell 命令，建立一個 SAN 憑證，加入 **\-DnsName** 參數即可，而且可以一次設定多組網域名稱。這裡由於沒有特別指定 **commonName** (**CN**) 名稱，預設會直接用 **\-DnsName** 參數的第一個項目當成憑證的**主體**名稱 ( a.k.a. commonName )。這裡的 **\-CertStoreLocation** 參數是指**憑證儲存區**的路徑。

> **New-SelfSignedCertificate -DnsName "www.fabrikam.com", "www.contoso.com" -CertStoreLocation "cert:\\LocalMachine\\My"**
> 
> \# 你也可以加上 **\-FriendlyName** 參數，指定一個 **易記名稱** (Friendly Name)，方便之後在 IIS 繫結憑證時可以容易識別哪一張憑證
> 
> **New-SelfSignedCertificate -DnsName "www.fabrikam.com", "www.contoso.com" -CertStoreLocation "cert:\\LocalMachine\\My" -FriendlyName MySSL**

   **請注意：**如果要建立憑證在 "**本機電腦**"，請記得 **以系統管理員身分執行** Windows PowerShell 才行。

3\. 匯出 **www.fabrikam.com** 憑證

4\. 匯入 **www.fabrikam.com** 憑證到**受信任的根憑證授權單位**

5\. 重新綁定 IIS 站台的 SSL 憑證

6\. 回到 Google Chrome 重新整理網頁，照理說就可以看到 HTTPS **綠色**安全的圖示了！

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/64e384bb948734f2c20c-image_thumb_263.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/6306cc9e2b998a1a083d-image_3131.png)

如果你測試用的自簽憑證到期了，透過 [New-SelfSignedCertificate](https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate) Cmdlet 的 **\-CloneCert** 參數可以很方便的快速**複製**原本的憑證，所有憑證的參數與設定都會沿用，只有憑證的**起訖時間**會重設，所以**重新產生憑證**非常方便！也因為是重新產生憑證，所以憑證內的 **公開金鑰** (Public Key) 也會是新的！

> **\# 先進入 cert:\\LocalMachine\\My 憑證目錄**
> 
> **cd "cert:\\LocalMachine\\My"**
> 
> **\# 列出所有憑證，找到憑證的憑證指紋 (Thumbprint)**
> 
> **dir**
> 
> **\# 取得憑證 ( 底下 059...688 這個為實際查到的憑證指紋 )**
> 
> **$cert = dir -Path 059A8EEF4EECCBB24FD25C65E86A15212F8FD688**
> 
> **\# 重新產生全新的憑證**
> 
> **New-SelfSignedCertificate -CloneCert $OldCert**

以下則是一個相對複雜一點的命令：

> **New-SelfSignedCertificate -Type Custom -Subject "CN=Patti Fuller,OU=UserAccounts,DC=corp,DC=contoso,DC=com" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2","2.5.29.17={text}upn=pattifuller@contoso.com") -KeyUsage DigitalSignature -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\\CurrentUser\\My"**

1\. 指定 **\-Type** 可以用來指定憑證預設的 **EKU** (Extended Key Usage)，合法的選項如下：

-   **SSLServerAuthentication (default)**

-   不設定 **\-Type** 預設就會選擇 **伺服器驗證 (serverAuth)** 與 **用戶端驗證 (clientAuth)**

-   CodeSigningCert
-   Custom

-   代表你想叫建立一個自定的 EKU 類別
-   設定為 Custom 必須搭配 **\-TextExtension** 指定 EKU 的 OID

-   DocumentEncryptionCert
-   DocumentEncryptionCertLegacyCsp

2\. 指定 **\-Subject** 參數可以幫你設定憑證的**主體資訊** (Subject)，也就是我們在建立 CSR 的時候輸入的那些資訊。

3\. 指定 **\-KeyUsage** 參數可以明確宣告這張憑證的**金鑰使用方法** (Key Usage)，預設為 **None** 不指定 (也就是什麼使用方法都可以的意思)。合法的選項如下：

-   CertSign
-   CRLSign
-   DataEncipherment
-   DecipherOnly
-   DigitalSignature
-   EncipherOnly
-   KeyAgreement
-   KeyEncipherment
-   **None (default)**
-   NonRepudiation

4\. 指定 **\-KeyAlgorithm** 參數可以設定建立 **非同步金鑰** (asymmetric keys) 時要採用哪種加密演算法，你可以選擇 [RSA](https://en.wikipedia.org/wiki/RSA_\(cryptosystem\)) 或 [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)。選擇 ECDSA 還必須透過 **certutil -displayEccCurve** 命令查詢出目前可用的 curvename。

5\. 指定 **\-KeyLength** 參數可以設定金鑰長度，常見設定為 2048 或 4096

**相關連結**

-   [New-SelfSignedCertificate](https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate) Cmdlet
-   [ERR\_CERT\_COMMON\_NAME\_INVALID on \*.local certs. - Google 產品論壇](https://productforums.google.com/forum/#!topic/chrome/zVo3M8CgKzQ;context-place=topicsearchin/chrome/category$3ACanary%7Csort:relevance%7Cspell:false)
-   [Subject Alternative Name - Wikipedia](https://en.wikipedia.org/wiki/Subject_Alternative_Name)
-   [Self signed certificate no longer valid as of Chrome 58 · Issue #854 · webpack/webpack-dev-server](https://github.com/webpack/webpack-dev-server/issues/854)
-   [Description of the Secure Sockets Layer (SSL) Handshake](https://web.archive.org/web/20190504223929/https://support.microsoft.com/en-us/help/257591/description-of-the-secure-sockets-layer-ssl-handshake)
