在.NET中操作數字證書(新手教程)


.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控制台自行添加。

  1. 開始  運行  MMC,打開一個空的MMC控制台。
  2. 在控制台菜單,文件  添加/刪除管理單元  添加按鈕  選"證書"  添加  選"我的用戶賬戶"  關閉  確定
  3. 在控制台菜單,文件  添加/刪除管理單元  添加按鈕  選"證書"  添加  選"計算機賬戶"  關閉  確定

如圖6-29,我們可以查看兩個賬戶的證書管理,在我的賬戶中可以看到MyTestContainer下的證書TestCert。

在MMC控制台查看和管理證書

當然我們也可以將證書文件保存為文件,如圖

圖:將證書保存為文件

打開E盤,可以看到生成的證書文件。如圖

圖生成的證書文件

將證書保存為文件時,我們有三種選擇:

  1. 帶有私鑰的證書

    由Public Key Cryptography Standards #12,PKCS#12標准定義,包含了公鑰和私鑰的二進制格式的證書形式,以pfx作為證書文件后綴名。

  2. 二進制編碼的證書

    證書中沒有私鑰,DER 編碼二進制格式的證書文件,以cer作為證書文件后綴名。

  3. 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上找到,我就不在重復了。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM