.NET為我們提供了運算元字證書的兩個主要的類,分為為:
System.Security.Cryptography.X509Certificates.X509Certificate2類, 每個這個類的例項可以表示一個證書;
System.Security.Cryptography.X509Certificates.X509Store類,可以對儲存在電腦保安區域內的證書進行add/remove/get操作。
另外我們可以使用System.Security.Cryptography.X509Certificates.X509Certificate2UI類來顯示證書訊息的對話方塊,它就是在IE中的證書檢視器的.NE實現。
生成證書
在介紹以上類的使用方法之前,我們先要擁有一個數字證書,獲取數字證書有三種方法,一是從CA機構申請,二是自己搭建伺服器釋出證書,三是使用makecert.exe來生成一個證書檔案。這裡我們使用makecert.exe來生成一個證書檔案,用來測試。啟動VS2010的命令列,輸入對應引數,生成名為TestCertificates的證書檔案。如圖
生成證書
makecert.exe的引數讀者可以檢視幫助,這裡只解釋圖6-28中的引數。
引數說明:
-sr CurrentUser:指定主題的證書儲存位置。Location 可以是 currentuser(預設值)或 localmachine
-ss MyTestContainer:指定主題的證書儲存名稱,輸出證書即儲存在那裡。
-n CN=TestCert:指定主題的證書名稱。此名稱必須符合 X.500 標準。最簡單的方法是在雙引號中指定此名稱,並加上字首 CN=;例如,"CN=myName"。
-sky exchange:指定頒發者的金鑰型別,必須是 signature、exchange 或一個表示提供程式型別的整數。預設情況下,可傳入 1 表示交換金鑰,傳入 2 表示簽名金鑰。
-pe:將所生成的私鑰標記為可匯出。這樣可將私鑰包括在證書中。
生成的金鑰檔案被儲存在了我們指定的MyTestContainer中,但到哪去檢視我們的證書呢?Windows沒有給我們準備好直接的管理證書的入口,但我們可以在MMC控制檯自行新增。
- 開始 執行 MMC,開啟一個空的MMC控制檯。
- 在控制檯選單,檔案 新增/刪除管理單元 新增按鈕 選"證書" 新增 選"我的使用者賬戶" 關閉 確定
- 在控制檯選單,檔案 新增/刪除管理單元 新增按鈕 選"證書" 新增 選"計算機賬戶" 關閉 確定
如圖6-29,我們可以檢視兩個賬戶的證書管理,在我的賬戶中可以看到MyTestContainer下的證書TestCert。
在MMC控制檯檢視和管理證書
當然我們也可以將證書檔案儲存為檔案,如圖
圖:將證書儲存為檔案
開啟E盤,可以看到生成的證書檔案。如圖
圖生成的證書檔案
將證書儲存為檔案時,我們有三種選擇:
-
帶有私鑰的證書
由Public Key Cryptography Standards #12,PKCS#12標準定義,包含了公鑰和私鑰的二進位制格式的證書形式,以pfx作為證書檔案字尾名。
-
二進位制編碼的證書
證書中沒有私鑰,DER 編碼二進位制格式的證書檔案,以cer作為證書檔案字尾名。
-
Base64編碼的證書
證書中沒有私鑰,BASE64 編碼格式的證書檔案,也是以cer作為證書檔案字尾名。
右鍵單擊本地的證書檔案,我們可以看到安裝選項,可以把該證書檔案安裝到證書儲存區。也可在MMC的證書管理臺上執行匯出任務將儲存區的證書匯出為檔案。這裡就不再演示了,讀者可以自行實踐。
程式設計操作證書
我們可以通過程式設計的方式操作操作本地的證書檔案和在儲存區中的證書。我們以剛才儲存在E盤的test.cer檔案為例,講解如何讀取本地的證書檔案,並將它新增到儲存區中。先看程式碼清單6-17。
程式碼清單 6-17 操作本地證書檔案
using System; using System.IO; using System.Security.Cryptography.X509Certificates; using NUnit.Framework; [TestFixture] public class OperCert { [Test] private byte[] ReadFile(string fileName) { FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read); int size = (int) f.Length; byte[] data = new byte[size]; size = f.Read(data, 0, size); f.Close(); return data; } [Test] public void CertTest() { try { X509Certificate2 x509 = new X509Certificate2(); byte[] rawData = ReadFile(@"e:\testc.cer"); x509.Import(rawData); Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine, x509.Subject); Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine, x509.Issuer); Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine, x509.Version); Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, x509.NotBefore); Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine, x509.NotAfter); Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine, x509.Thumbprint); Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine, x509.SerialNumber); Console.WriteLine("{0}Friendly Name: {1}{0}", Environment.NewLine, x509.PublicKey.Oid.FriendlyName); Console.WriteLine("{0}Public Key Format: {1}{0}", Environment.NewLine, x509.PublicKey.EncodedKeyValue.Format(true)); Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine, x509.RawData.Length); Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine, x509.ToString(true)); Console.WriteLine("{0}Certificate to XML String: {1}{0}", Environment.NewLine, x509.PublicKey.Key.ToXmlString(false)); X509Store store = new X509Store(); store.Open(OpenFlags.MaxAllowed); store.Add(x509); store.Close(); } catch (Exception e) { Console.WriteLine("Error:" + e.Message); } } }
輸入瞭如下內容:
Subject: CN=Joe's-Software-Emporium
Issuer: CN=Root Agency
Version: 3
Valid Date: 2013-6-28 13:12:48
Expiry Date: 2040-1-1 7:59:59
Thumbprint: 269ED45BCC22ABB2E416B19E16CBC68E1045DCDD
Serial Number: D6D43B334428E0BD40E148EE641524C0
Friendly Name: RSA
Public Key Format: 30 81 89 02 81 81 00 b7 a3 d1 9a 73 a3 f3 b0 1d 72 1e 5b 67 d2 b9 a6 4c f2 9f 01 7c 9d 7d 81 a5 7f b8 b6 54 fe 29 53 91 0c 99 60 01 89 43 2d 61 9f 4e f6 49 5f 9e 66 51 e3 cc 7d 6e 45 73 65 ef 09 c1 37 71 5c 1a 64 06 b7 ac ef 9f 50 68 8c a9 48 43 d0 7c d0 c5 01 c3 77 9d e5 b1 d4 83 d9 15 83 27 f0 2b f0 00 9a e0 10 2c 9c 6f 97 77 f1 e2 d3 f8 68 17 1c c5 5e a6 ec 03 a4 5c df 93 5e 88 46 fa 5d f7 76 9d bd 02 03 01 00 01
Raw Data Length: 451
Certificate to string: [Version]
V3
[Subject]
CN=Joe's-Software-Emporium
Simple Name: Joe's-Software-Emporium
DNS Name: Joe's-Software-Emporium
[Issuer]
CN=Root Agency
Simple Name: Root Agency
DNS Name: Root Agency
[Serial Number]
D6D43B334428E0BD40E148EE641524C0
[Not Before]
2013-6-28 13:12:48
[Not After]
2040-1-1 7:59:59
[Thumbprint]
269ED45BCC22ABB2E416B19E16CBC68E1045DCDD
[Signature Algorithm]
sha1RSA(1.3.14.3.2.29)
[Public Key]
Algorithm: RSA
Length: 1024
Key Blob: 30 81 89 02 81 81 00 b7 a3 d1 9a 73 a3 f3 b0 1d 72 1e 5b 67 d2 b9 a6 4c f2 9f 01 7c 9d 7d 81 a5 7f b8 b6 54 fe 29 53 91 0c 99 60 01 89 43 2d 61 9f 4e f6 49 5f 9e 66 51 e3 cc 7d 6e 45 73 65 ef 09 c1 37 71 5c 1a 64 06 b7 ac ef 9f 50 68 8c a9 48 43 d0 7c d0 c5 01 c3 77 9d e5 b1 d4 83 d9 15 83 27 f0 2b f0 00 9a e0 10 2c 9c 6f 97 77 f1 e2 d3 f8 68 17 1c c5 5e a6 ec 03 a4 5c df 93 5e 88 46 fa 5d f7 76 9d bd 02 03 01 00 01
Parameters: 05 00
[Extensions]
* 頒發機構金鑰識別符號(2.5.29.1):
KeyID=12 e4 09 2d 06 1d 1d 4f 00 8d 61 21 dc 16 64 63
Certificate Issuer:
CN=Root Agency
Certificate SerialNumber=06 37 6c 00 aa 00 64 8a 11 cf b8 d4 aa 5c 35 f4
Certificate to XML String: <RSAKeyValue><Modulus>t6PRmnOj87Adch5bZ9K5pkzynwF8nX2BpX+4tlT+KVORDJlgAYlDLWGfTvZJX55mUePMfW5Fc2XvCcE3cVwaZAa3rO+fUGiMqUhD0HzQxQHDd53lsdSD2RWDJ/Ar8ACa4BAsnG+Xd/Hi0/hoFxzFXqbsA6Rc35NeiEb6Xfd2nb0=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
程式碼清單6-17演示瞭如何讀取本地證書檔案的方法。靜態方法ReadFile用來從本地磁碟中讀取證書檔案到byte陣列中。主要的操作都在Main方法中。X509Certificate2 x509 = new X509Certificate2()一句使用無引數的建構函式初始化X509Certificate2類的例項x509。然後我們使用x509.Import(rawData)語句將byte陣列匯入到當前證書例項。接下來是輸出該證書的資訊。
輸出資訊之後,我們看下面的四行程式碼:
X509Store store = new X509Store(); store.Open(OpenFlags.MaxAllowed); store.Add(x509); store.Close();
首先我們初始化一個X509Store類的例項store,然後使用Open方法開啟儲存區,新增上面讀取的證書到儲存區。
X509Certificate2一共提供了14個建構函式供我們使用,這裡就不一一介紹了。我們也可以通過X509Certificate2類的建構函式直接匯入本地的證書檔案,可以使用程式碼清單6-18所示的方式。
程式碼清單6-18 使用建構函式匯入證書檔案
X509Certificate2 myX509Certificate2 = new X509Certificate2( @"e:\MyTestCert.pfx", //證書路徑 "password", //證書的私鑰保護密碼 X509KeyStorageFlags.Exportable //表示此證書的私鑰以後還可以匯出 );
程式碼清單6-18給出瞭如何匯入帶私鑰保護密碼的證書的方法。X509KeyStorageFlags 列舉用來標識X.509 證書的私鑰匯出到何處以及如何匯出。該列舉的成員說明如表6-1所示。
表6-1 X509KeyStorageFlags 列舉說明
|
說明 |
DefaultKeySet |
使用預設的金鑰集。使用者金鑰集通常為預設值。 |
UserKeySet |
私鑰儲存在當前使用者儲存區而不是本地計算機儲存區。既使證書指定金鑰應儲存在本地計算機儲存區,私鑰也會儲存到當前使用者儲存區。 |
MachineKeySet |
私鑰儲存在本地計算機儲存區而不是當前使用者儲存區。 |
Exportable |
匯入的金鑰被標記為可匯出。 |
UserProtected |
通過對話方塊或其他方法,通知使用者金鑰被訪問。使用的加密服務提供程式 (CSP) 定義確切的行為。 |
PersistKeySet |
匯入證書時會儲存與 PFX 檔案關聯的金鑰。 |
那麼如何操作儲存區中的證書呢,可以使用程式碼清單6-19的方式。
程式碼清單6-19 操作儲存區中的證書
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); //輪詢儲存區中的所有證書 foreach(X509Certificate2 myX509Certificate2 in store.Certificates) { //將證書的名稱跟要匯出的證書MyTestCert比較,找到要匯出的證書 if (myX509Certificate2.Subject == "CN=TestCert") { //證書匯出到byte[]中,password為私鑰保護密碼 byte[] CertByte = myX509Certificate2.Export(X509ContentType.Pfx,"password"); //將證書的位元組流寫入到證書檔案 FileStream fStream = new FileStream( @"C:\Samples\PartnerAEncryptMsg\MyTestCert_Exp.pfx", FileMode.Create, FileAccess.Write); fStream.Write(CertByte, 0, CertByte.Length); fStream.Close(); } } store.Close();
程式碼清單6-19首先宣告X509Store類的例項store,使用了兩個引數的建構函式,第一個引數是儲存容器的名稱,StoreName列舉只能列舉系統預設的儲存區名稱。第二個引數是StoreLocation列舉,用來標識是本機證書還是當前使用者證書。匯出容器證書使用的是Export方法。第一個引數X509ContentType.Pfx表示要匯出為含有私鑰的pfx證書形式,第二個引數為私鑰保護密碼。如果要匯出為不含私鑰的cer證書,第一個引數使用X509ContentType.Cert,表示匯出為不含私鑰的cer證書,也就不需要密碼了。
建立發行者證書
發行者證書是驗證發行者可靠性的證書檔案,保護證書發行者的簽名。我們可以從證書頒發機構獲得該檔案。做為程式測試,我們可以使用Cert2spc.exe來生成發行者證書。從命令列啟動該程式,如圖6-32所示。
圖6-32 生成SPC檔案
如圖6-32,我們使用Cert2spc.exe以test.cer為引數生成目標為tset.spc的發行者證書,如果存在多個證書檔案,可以作為引數以空格隔開生成統一的發行者證書。
使用證書對檔案簽名
簽名工具 (SignTool.exe) 是一個命令列工具,用於對檔案進行數字簽名,驗證檔案或時間戳檔案中的簽名。我們可以對cab檔案、dll檔案或者其他檔案進行簽名,從網際網路訪問這些檔案的時候就需要安裝和驗證證書。該工具詳細的說明讀者可以從MSDN上找到,我就不在重複了。