程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET安全: 使用.NET Framework 2.0在您的應用程序中支持證書

.NET安全: 使用.NET Framework 2.0在您的應用程序中支持證書

編輯:關於.NET

本文討論:

Windows 證書存儲區

.NET 中的證書類

驗證、SSL、Web 服務和代碼 簽名

對數據進行簽名和加密

本文使用了以下技術:

.NET Framework 2.0

證 書在 Microsoft® .NET Framework 中應用十分廣泛,從安全通信到代碼簽名再到安全策略。.NET Framework 2.0 改進了對證書的支持,為使用證書進行符合標准的加密操作添加了一個全新的命名空間。 在本文中,我將討論證書和 Windows® 證書存儲區的背景知識。同時我還會為您介紹證書 API 的使 用方法和 Framework 如何使用這些 API 實現安全功能。

“證書”實際上是一種 ASN.1 (Abstract Syntax Notation One) 編碼的文件,它包含一 個公鑰和其他有關該密鑰及其所有者的信息。另外,證書具有有效期,並通過另一密鑰(所謂的頒發者) 進行簽名,該密鑰能保證這些屬性的真實性,最重要的是,保證公鑰本身的真實性。您可以將 ASN.1 看 成是一種二進制 XML。與 XML 一樣,它也具有編碼規則、強類型和標記;但是,這些都是二進制值,通 常沒有可打印字符與之對應。

要使這種文件能夠在系統之間互換,需要一種標准的格式。這種標 准格式即 X.509(當前為第 3 版),RFC 3280 (tools.ietf.org/html/rfc3280)) 中對其進行了描述。 雖然 X.509 並未規定證書中嵌入的密鑰類型,但 RSA 算法是目前使用最為普遍的非對稱加密算法。

首先讓我們回顧一下這種算法的歷史由來。“RSA”這一名稱是發明該算法的三個人的姓氏 首字母縮寫:Ron Rivest、Adi Shamir 和 Len Adleman。他們成立了一家名為 RSA Security 的公司, 該公司發布了幾個名為公鑰加密標准 (PKCS) 的標准文檔。這些文檔對加密技術的幾個方面進行了介紹。

其中最流行的文檔之一,即 PKCS #7,為已簽名和加密的數據定義了一種名為加密消息語法 (CMS) 的二進制格式。目前 CMS 廣泛應用於眾多流行的安全協議,其中包括安全套接字層 (SSL) 和安全 多用途 Internet 郵件擴展 (S/MIME)。由於它是一種標准,因此當應用程序需要在幾方之間交換已簽名 和加密的數據時,它也是一種可供選擇的格式。您可以從 RSA Laboratories 網站 (www.rsasecurity.com/rsalabs/node.asp?id=2124) 獲得這些 PKCS 文檔。

如何獲得一個證書

目前有幾種方法可以獲取證書。在交換文件時,證書通常以兩種格式之一出 現。擴展名為 .cer 的文件是采用 X.509v3 格式的已簽名 ASN.1 文件。這些文件中包含著我之前提到的 一個公鑰和額外的信息。您要將這些文件中包含的內容提供給業務合作伙伴或朋友,以便他們能夠使用公 鑰為您加密數據。

此外,您可能會遇到擴展名為 .pfx(個人信息交換,Personal Information Exchange)的文件。.pfx 文件包含一個證書和與之對應的私鑰(PKCS #12 標准對該格式有所說明)。這 類文件是高度敏感的,通常用於導入服務器上的密鑰對或用於備份目的。在導出密鑰對時,Windows 提供 用密碼加密 .pfx 文件;而在導入密鑰對時,您必須再次提供此密碼方可導入。

您還可以生成自 己的證書。證書的生成方式通常取決於其使用方式。在對等方身份不明的常規 Internet 環境下,您通常 要向某個商業證書頒發機構 (CA) 申請證書。該方法的優點在於這些已知的 CA 已經得到 Windows 和其 他任何支持證書及 SSL 的 OS(包括浏覽器)的信任。因此不必進行 CA 密鑰的交換。

對於 B2B 和 Intranet 環境,您可以使用內部 CA。Windows 2000 和 Windows Server® 2003 中包含了證書服 務。配合 Active Directory® 一起使用,此功能允許您在組織內輕松地分發證書。(稍後我將介紹 如何從私有 CA 申請證書。)

在開發過程中,有時您可能會發現,剛才提到的方法不起作用了。例如,如果您出於測試的需要希望 很快獲得一個證書,可以使用 makecert.exe。該工具包隨附於 .NET Framework SDK 中,能夠生成證書 和密鑰對。在 IIS 資源工具包中也有一個與之類似的名為 selfssl.exe 的工具。它專門用於創建 SSL 密鑰對,而且使用這種密鑰,只需一個步驟即可對 IIS 進行配置。

Windows 證書存儲區

證書和與之對應的私鑰可存儲在各種設備上,例如硬盤、智能卡和 USB 令牌。Windows 提供了一個名為 證書存儲區的抽象層,用於統一證書的訪問方式,不管這些證書存儲在何處。只要硬件設備具有 Windows 支持的加密服務提供程序 (CSP),就可以使用證書存儲區 API 訪問其上存儲的數據。

證書存儲區 位於用戶配置文件的深處。這樣就可以對特定帳戶的密鑰使用 ACL。每個存儲區被劃分為若干個容器。例 如,其中有一個名為 Personal 的容器,您可以將自己的證書(具有關聯私鑰的證書)存儲在其中。 Trusted Root Certification Authorities 容器包含了所有您信任的 CA 的證書。Other People 容器則 保存著與您進行安全通信的人員的證書。此外還有其他一些證書。訪問證書存儲區最簡單方法是運行 certmgr.msc。

另外還有一個供 Windows 計算機帳戶(NETWORK、LOCAL SERVICE 和 LOCAL SYSTEM) 使用的計算機范圍的存儲區,如果您希望跨帳戶共享證書或密鑰,可以使用該存儲區。 ASP.NET 應用程序總是使用計算機存儲區;而對於桌面應用程序,證書通常安裝在用戶存儲區。

只有管理員才能對計算機存儲區和服務帳戶存儲區進行管理。要實現這一目的,您必須啟動 Microsoft 管理控制台 (mmc.exe) 並添加“證書”管理單元,從中可以選擇要管理的存儲區。圖 1 顯示 了 MMC 管理單元的一個屏幕快照。

圖 1“證書 ”MMC 管理單元

除了可以導入、導出和搜索證書外,您還可以通過管理單元從內部企業 CA 申請證書。只需右鍵單擊 Personal 容器並選擇 All Tasks | Request Certificate。本地計算機會隨後 生成一個 RSA 密鑰對,並將公鑰部分發送給 CA 以進行簽名。Windows 將已簽名的證書添加到證書存儲 區,並將對應的私鑰添加到密鑰容器。證書通過存儲屬性被鏈接到密鑰容器。

對於對應帳戶或 LOCAL SYSTEM,私鑰容器受到 ACL 的嚴密保護。當您要從 ASP.NET 或其他用戶帳戶訪問計算機配置文件 中存儲的密鑰時,這就成為一個問題。為此我編寫了一個工具,您可以用它修改容器文件的 ACL (可從 www.leastprivilege.com/HowToGetToThePrivateKeyFileFromACertificate.aspx 下載該工具)。

商業 CA 和 Windows CA 還為申請證書提供了 Web 界面。通常在這些時候,由 Internet Explorer® 中的 ActiveX® 控件生成密鑰並將其導入當前用戶的存儲區中。按照慣例,當您想指 定某個證書可以由某個用戶或服務訪問時,有以下兩種選擇:一是將該證書導入該用戶的存儲區,二是在 以該用戶身份登錄時申請證書。

使用證書

證書可用於 .NET Framework 中的各個位置,而 且在某種程度上此功能依賴於 System.Security.X509Certificates 命名空間中的 X509Certificate 類 。如果您更仔細地觀察,還會發現證書類是以 2 結尾。這是因為 .NET Framework 1.x 具有一個名為 X509Certificate 的 X.509 證書表示形式。該類的功能有限,而且不支持加密操作。2.0 版中新添加了 一個名為 X509Certificate2 的類。該類派生自 X509Certificate,同時添加了許多功能。您可以根據需 要在二者之間來回轉換,但無論何時都應盡可能使用最新版本。

訪問證書

您可以直接從文 件系統檢索證書。但更好的辦法是從證書存儲區進行檢索。要從一個 .cer 文件創建一個 X509Certificate2 實例,只需將文件名傳遞給構造函數即可:

X509Certificate2 cert1 = new 

X509Certificate2("alice.cer");

您也可以從 .pfx 文件加載證書。但是,如前所 述,.pfx 文件可以通過設置密碼加以保護,因此您應當將該密碼以 SecureString 的形式提供給他人。 SecureString 在內部對密碼進行加密,並盡可能降低密碼在內存、頁面文件和崩潰轉儲期間的暴露幾率 。因此,您每次只能向字符串添加一個(值類型)字符。如果要從控制台要求用戶提供密碼,那麼圖 2 中的代碼是非常有用的,這些代碼可禁用控制台回顯並返回 SecureString。

Figure 2 從控制台申請密碼

private SecureString GetSecureStringFromConsole()
{
  SecureString password = new SecureString();
  Console.Write("Enter Password: ");
  while (true)
  {
    ConsoleKeyInfo cki = Console.ReadKey(true);
    if (cki.Key == ConsoleKey.Enter) break;
    else if (cki.Key == ConsoleKey.Escape)
    {
      password.Dispose();
      return null;
    }
    else if (cki.Key == ConsoleKey.Backspace)
    {
      if (password.Length != 0)
        password.RemoveAt(password.Length - 1);
    }
    else password.AppendChar(cki.KeyChar);
  }
  return password;
}

在“使用 .NET Framework 2.0 進行加密管理”(可從  msdn.microsoft.com/library/en-us/dnnetsec/html/credmgmt.asp 獲得)一文中,Kenny Kerr 談到了 將常見 Windows 憑據對話框的結果轉換為 SecureString 的代碼。不管您以何種方式獲得 SecureString ,它都可以隨後被傳遞到 X509Certificate2 構造函數,以加載 .pfx 文件,如下所示:

X509Certificate2 cert2 = new X509Certificate2("alice.pfx", 

password);

要訪問 Windows 證書存儲區,請使用 X509Store 類。在其構造函數中,您要提供 存儲區位置(當前用戶或計算機)和存儲區名稱。可以使用字符串或 StoreName 枚舉來指定要打開的容 器。注意,內部名稱並不總是與 MMC 管理單元中找到的名稱匹配。Personal 容器映射到名稱 My,而 Other People 則變為 AddressBook。

獲得有效的 X509Store 實例後,就可以搜索、檢索、刪除 和添加證書。除非在部署情況下,否則使用最為頻繁的恐怕要數搜索功能。您可以按各種條件搜索證書, 其中包括使用者名稱、序列號、指紋、頒發者和有效期。如果以編程方式從存儲區檢索應用程序中的證書 ,則應當使用唯一的屬性,例如接收者密鑰標識符。指紋雖然也是唯一的,但要記住,它是證書的一個 SHA-1 哈希值,在諸如續訂證書時會發生改變。圖 3 中的代碼顯示了一種搜索證書的常規方法。

Figure 3 搜索證書

static void Main(string[] args)
{
  // search for the subject key id
  X509Certificate2 cert = FindCertificate(
   StoreLocation.CurrentUser, StoreName.My,
   X509FindType.FindBySubjectKeyIdentifier,
   "21f2bf447298e83056a69eb02ebe9085ed97f10a");
}
static X509Certificate2 FindCertificate(
  StoreLocation location, StoreName name,
  X509FindType findType, string findValue)
{
  X509Store store = new X509Store(name, location);
  try
  {
    // create and open store for read-only access
    store.Open(OpenFlags.ReadOnly);
    // search store
    X509Certificate2Collection col = store.Certificates.Find(
     findType, findValue, true);
    // return first certificate found
    return col[0];
  }
    // always close the store
  finally { store.Close(); }
}

在獲得 X509 Certificate2 的一個實例後,可以檢查證書的各個屬性(如使用者名稱、到期 日期、頒發者和友好名稱)。HasPrivateKey 屬性會告知您是否存在關聯私鑰。PrivateKey 和 PublicKey 屬性將對應密鑰作為一個 RSACryptoServiceProvider 實例返回。

要導入證書,請對 X509Store 實例調用 Add 方法。當存儲區的構造函數中不存在您所指定的存儲區名稱時,就會創建一個 新容器。以下說明了如何將名為 alice.cer 的文件中的一個證書導入到名為 Test 的新容器中:

static void ImportCert()
{
  X509Certificate2 cert = new X509Certificate2("alice.cer");
  X509Store store = new X509Store("Test", StoreLocation.CurrentUser);
  try
  {
    store.Open(OpenFlags.ReadWrite);
    store.Add(cert);
  }
  finally { store.Close(); }
}

顯示證書詳細信息和證書選擇器

Windows 提供了兩個標准對話框對證書進行操作:其 中一個對話框用於顯示證書的詳細信息(各個屬性和證書路徑),另一個則供用戶從列表中選擇證書。您 可以使用 X509Certificate2UI 類的兩個靜態方法訪問這兩個對話框:SelectFromCollection 和 DisplayCertificate。

要顯示證書列表,必須填充 X509Certificate2Collection 並將其傳遞給 SelectFromCollection。讓 用戶從存儲區內的個人證書之一進行選擇是很常見的。為此,只需傳入一個已打開的 X509Store 的 Certificates 屬性即可。您還可以控制對話框標題、消息以及是否允許多個選擇。DisplayCertificate 方法顯示的對話框與在 Windows 資源管理器中雙擊 .cer 文件時看到的對話框相同。圖 4 顯示了選擇證 書所用的對話框,圖 5 提供相應的代碼。

Figure 5 用於選擇證書的代碼

private static X509Certificate2 PickCertificate(
 StoreLocation location, StoreName name)
{
  X509Store store = new X509Store(name, location);
  try
  {
    store.Open(OpenFlags.ReadOnly);
    
    // pick a certificate from the store
    X509Certificate2 cert = 
      X509Certificate2UI.SelectFromCollection(
        store.Certificates, "Caption",
        "Message", X509SelectionFlag.SingleSelection)[0];
    // show certificate details dialog
    X509Certificate2UI.DisplayCertificate(cert);
    return cert;
  }
  finally { store.Close(); }
}

圖 4 用於選擇證書的對話框

驗證證書

驗證證書時需要考慮幾個條件,尤其是頒發方(通常僅信任可信 CA 列表中的 CA 所 頒發的證書)及其當前有效性(證書可能會失效,例如當過期或者被頒發證書的 CA 吊銷時)。 X509Chain 類可以用來檢查這些不同屬性。使用該類,您可以為有效性檢查指定策略,例如,可以要求一 個受信任根 CA 或指定是否進行聯機檢查或檢查本地吊銷列表。如果需要對用於數據簽名的證書進行檢查 ,則在計算簽名時檢查證書是否有效就變得很重要;為此,X509Chain 允許更改驗證時間。

構造 策略後,可以調用 Build 方法來獲取有關 ChainStatus 屬性驗證結果的信息。如果出現多個驗證錯誤, 您可以循環訪問 ChainElement 集合以獲取更多詳細信息。圖 6 顯示了如何根據脫機和聯機吊銷列表對 證書及其頒發者執行嚴格的驗證。

Figure 6 嚴格的證書驗證

static void ValidateCert(X509Certificate2 cert)
{
  X509Chain chain = new X509Chain();
  
  // check entire chain for revocation
  chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
  // check online and offline revocation lists
  chain.ChainPolicy.RevocationMode =
    X509RevocationMode.Online | X509RevocationMode.Offline;
  // timeout for online revocation list
  chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);
  // no exceptions, check all properties
  chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
  // modify time of verification
  //chain.ChainPolicy.VerificationTime = new DateTime(1999, 1, 1);
  chain.Build(cert);
  if (chain.ChainStatus.Length != 0)
    Console.WriteLine(chain.ChainStatus[0].Status);
}

SSL 支持

SSL 身份驗證協議依賴於證書。.NET Framework 中對 SSL 的支持包含兩個 部分。HTTP 上的 SSL 這種特殊情況(但使用最為廣泛)由 HttpWebRequest 類(它最終還可用於 Web 服務客戶端代理)實現。要啟用 SSL,除了要指定一個使用 Https: 協議的 URL 外,不必執行任何特殊 操作。

當連接到一個受 SSL 保護的終結點時,會在客戶端上對服務器證書進行驗證。如果驗證失 敗,連接會根據默認設置立即關閉。您可以回調一個名為 ServicePointManager 的類來重寫該行為。每 當 HTTP 客戶端的堆棧進行證書驗證時,都會首先檢查是否可以回調;如果可以,則執行您的代碼。要掛 接該回調,您必須提供類型 RemoteCertificateValidationCallback 的一個委托:

// 

override default certificate policy
// (for example, for testing purposes)
ServicePointManager.ServerCertificateValidationCallback = 
  new RemoteCertificateValidationCallback(VerifyServerCertificate);

在回調中,您會 獲得服務器證書、一個錯誤代碼和一個傳入的鏈對象,然後可以執行自己的檢查並返回 true 或 false。 如果出現諸如證書在開發或測試期間就已過期的情況,那麼關閉其中某項檢查是有好處的。另一方面,這 樣做還可以執行比默認更為嚴格的驗證策略。圖 7 提供了一個驗證回調的示例。

Figure 7 驗證回調

private bool VerifyServerCertificate(
    object sender, X509Certificate certificate,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None) return true;
    foreach (X509ChainStatus s in chain.ChainStatus)
    {
        // allows expired certificates
        if (string.Equals(s.Status.ToString(), "NotTimeValid",
            StringComparison.OrdinalIgnoreCase))
                return true;
    }
    return false;
}

SSL 還支持使用證書對客戶端進行身份驗證。如果您要訪問的網站或服務要求提供客戶端證書,那麼 Web 服務客戶端代理和 HttpWebRequest 都可提供 X509Certicate 類型的 ClientCertificates 屬性:

proxy.Url =
"https://server/app/service.asmx";
proxy.ClientCertificates.Add(
PickCertificate(...));

此外,.NET Framework 2.0 還引入了一個名為 SslStream 的新類。該類允許您在任何流的頂部設置 SSL 層,而不僅限於 HTTP,從而可以對基於套接字的自定義協議啟用 SSL。SslStream 將標准 .NET 證 書支持應用於各個方面,例如,使用我所討論的驗證回調機制:

public SslStream(Stream innerStream, bool leaveInnerStreamOpen,
RemoteCertificateValidationCallback ValidationCallback) {...}

要使用 SslStream 啟動 SSL 身份驗證,請將 X509Certificate 傳遞給其 AuthenticateAsServer 方法:

ssl.AuthenticateAsServer(PickCertificate(...));

Web 服務安全性

WS 安全性標准使用證書來指定客戶端和服務器的身份驗證並確保通信安全。諸如 .NET Framework 的 Web 服務增強 (WSE) 的各種工具包以及諸如 Windows Communication Foundation 的各項技術都對此提 供了完全的支持。同樣,最終還是要通過代碼或配置來提供證書。以下代碼段說明了如何使用 WSE3 向 Web 服務代理添加一個客戶端證書:

X509SecurityToken token = new X509SecurityToken(PickCertificate(...));

proxy.RequestSoapContext.Security.Tokens.Add(token);

對於 Windows Communication Foundation,通常要引用配置文件中的證書存儲區(參見圖 8)。如您 所見,所有配置屬性都直接映射到在前面代碼中使用的枚舉。

Figure 8 在 WCF 中提供證書引用

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="ServiceBehavior">
        <serviceCredentials>
          <serviceCertificate storeLocation="LocalMachine"
            storeName="My" x509FindType="FindBySubjectKeyIdentifier"
            findValue="1a7b..." />
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

安全策略和代碼簽名

證書還應用於 Authenticode 代碼簽名。通過對二進制文件進行簽名,您可以添加有關發布者的信息 ,並確保簽名完成後該簽名文件能得到可靠驗證。.NET Framework SDK 中的 signtool.exe 工具可用於 對 .exe 文件和 .dll 文件進行簽名。簽名後,使用 Windows 資源管理器中的屬性對話框驗證簽名並查 看證書。注意,如果要同時使用 Authenticode 簽名和強名稱簽名,需要首先應用強名稱簽名。此外,在 加載時,Authenticode 簽名的程序集可能會出現延遲。如果被簽名位置為可執行文件的入口點,則延遲 會導致應用程序的啟動時間延長。

已簽名文件還可用於安全策略。使用軟件限制策略,您可以根據簽名或缺少簽名來限制非托管可執行 文件的執行(請參閱 microsoft.com/technet/prodtechnol/winxppro/maintain/rstrplcy.mspx)。而且 .NET Framework 代碼訪問安全 (CAS) 策略可以根據發行者證書來支持代碼組。

要創建一個 CAS 策略,請根據發行者成員條件使用 mscorcfg.msc 創建一個新的代碼組。然後將一個 權限集分配給由該發行者簽名的所有應用程序(參見圖 9)。

圖 9 將權限分配給發行者

ClickOnce 清單

另一種將證書用於發行者信息的技術是 ClickOnce。在發布一個 ClickOnce 應用程序時,您必須對部 署和應用程序清單進行簽名。這會再次將發行者信息添加到應用程序,並確保清單中的敏感信息只有在簽 名失效的情況下才能被修改(如安全策略和應用程序依賴項)。在安裝過程中,ClickOnce 使客戶端能夠 使用發行者信息,以便客戶可以對應用程序的可信性做出明智的決策。根據證書及其驗證結果的不同, ClickOnce 安裝程序還使用不同的可視化提示。圖 10 顯示了 Visual Studio 清單簽名對話框。

圖 10 Visutal Studio 清單簽名對話框

對數據進行簽名和加密

到目前為止,我主要介紹了與證書有關的基礎 API 以及其他技術如何使用這些 API。現在我想與大家 討論一下加密操作(如使用證書對數據加密和簽名)和如何實現 .NET Framework 2.0 中新發現的 PKCS #7。

數據的保護始終分為兩個步驟。首先,對數據進行簽名以防止其被篡改。然後,對數據進行加密以防 止其被洩漏。但是,在使用 PKCS #7 類執行任何加密操作之前,都必須先將數據包裝在一個 ContentInfo 對象中,表示為一個 CMS 數據結構。數據將從這裡轉換為已簽名或加密的數據,分別由 SignedCms 和 EnvelopedCms 類來表示。

從技術上講,數字簽名是對隨後使用私鑰加密的數據的哈希。這意味著您需要一個含有關聯私鑰或 .pfx 文件的證書。您可以根據這種證書創建一個代表數據簽名者的 CmsSigner 對象。接著 SignedCms 類會對簽名進行計算並輸出一個符合 PKCS #7 和 CMS 的字節數組。圖 11 顯示了對應的代碼。已編碼的 字節數組包含了您的數據、簽名和用於對數據簽名的證書。

Figure 11 輸出符合 CMS 的字節數組

byte[] Sign(byte[] data, X509Certificate2 

signingCert)
{
    // create ContentInfo
    ContentInfo content = new ContentInfo(data);
    // SignedCms represents signed data
    SignedCms signedMessage = new SignedCms(content);
    // create a signer
    CmsSigner signer = new CmsSigner(signingCert);
    // sign the data
    signedMessage.ComputeSignature(signer);
    // create PKCS #7 byte array
    byte[] signedBytes = signedMessage.Encode();
    // return signed data
    return signedBytes;
}

如果您要對大量數據簽名,這樣做可能沒有什麼;但如果要簽名的數據量很小,則會增加一些開銷。 例如,使用 2KB 公鑰對一個 10 字節數組進行簽名會產生一個大約 2,400 字節的數組。如果您打算將已 簽名數據存儲在(比如說)數據庫中,那麼請牢記這一點。另一種方法是使用所謂的分離簽名。這種方法 使您能夠從簽名中刪除數據並單獨進行存儲。例如,您可以先將多個小數據段加以組合,然後統一對其進 行簽名。要創建一個分離簽名,必須向 SignedCms 構造函數再傳遞一個 true 值,如圖 12 所示。

Figure 12 創建分離簽名

byte[] SignDetached(byte[] data, X509Certificate2 

signingCert)
{
    // create ContentInfo
    ContentInfo content = new ContentInfo(data);
    // pass true to the constructor to indicate
    // we want to sign detached
    SignedCms signedMessage = new SignedCms(content, true);
    // these steps are the same 
    CmsSigner signer = new CmsSigner(signingCert);
    signedMessage.ComputeSignature(signer);
    byte[] signedBytes = signedMessage.Encode();
    // return only the signature (not the data)
    return signedBytes;
}

對數據進行簽名後,即可對其加密。您需要接收者的公鑰才能對數據進行解密。通常這些公鑰可以從 Other People 存儲區獲得,但如果不希望使用證書存儲區,也可以從 .cer 文件獲取公鑰。這樣一來, 所有繁重工作就交給 EnvelopedCms 類來執行。您需要在 CmsRecipientCollection 中指定一個用於加密 的公鑰並將其傳遞到 Encrypt 方法中。與 SignedCms 一樣,Encode 方法會在此處創建符合 PKCS #7 和 CMS 的字節數組(參見圖 13)。

Figure 13 Encode 方法

byte[] Encrypt(byte[] data, X509Certificate2 encryptingCert)
{
// create ContentInfo
ContentInfo plainContent = new ContentInfo(data);
// EnvelopedCms represents encrypted data
EnvelopedCms encryptedData = new EnvelopedCms(plainContent);
// add a recipient
CmsRecipient recipient = new CmsRecipient(encryptingCert);
// encrypt data with public key of recipient
encryptedData.Encrypt(recipient);
// create PKCS #7 byte array
byte[] encryptedBytes = encryptedMessage.Encode();
// return encrypted data
return encryptedBytes;
}

EnvelopedCms 在內部生成一個隨機會話密鑰,數據借助該密鑰被對稱加密。隨後再使用每個接收者的 公鑰對該會話密鑰進行加密。這樣一來,您就無需為每個收件人單獨保留一份數據的加密版本。另外數據 中還嵌入了一些額外的信息,使接收者能夠從自己的證書存儲區中找到匹配的私鑰來解密數據。

解密數據和驗證簽名

接收端的整個解密過程是與加密過程是截然相反的。也就是說,首先要將數據解密,然後再驗證簽名 和簽名證書。在代碼中,您必須首先調用 SignedCms 和 EnvelopedCms 類的 Decode 方法,將 CMS 字節 數組反序列化為對象表示形式。然後再分別調用 Decrypt 和 CheckSignature。

該過程會查看已加密的數據包,以判斷是否可以通過在證書存儲區中搜索對應的私鑰來解密會話密鑰 。隨後再使用已解密的會話密鑰對實際數據進行解密。您也可以提供一個解密期間要加以考慮的額外證書 的列表,以防私鑰並未存儲在證書存儲區:

static byte[] Decrypt(byte[] data)
{
// create EnvelopedCms
EnvelopedCms encryptedMessage = new EnvelopedCms();
// deserialize PKCS#7 byte array
encryptedMessage.Decode(data);
// decryt data
encryptedMessage.Decrypt();
// return plain text data
return encryptedMessage.ContentInfo.Content;
}

驗證數據的過程分為兩步。首先,確保簽名是有效的,也就是說數據是未被篡改的。然 後檢查簽名證書。使用 SignedCms 類的 CheckSignature 方法可同時執行這兩步操作在這種情況下,證 書會比照默認的系統策略被驗證。如果您希望對該過程進行更大程度的控制,可以使用 X509Chain 對象 和代碼(如圖 6 所示)親自進行檢查。圖 14 顯示了用於檢查和刪除簽名的代碼,而圖 15 提供了用於 驗證分離簽名的代碼。

Figure 15 分離簽名驗證

static bool VerifyDetached(byte[] data, byte[] signature)
{
    ContentInfo content = new ContentInfo(data);
    // pass true for detached
    SignedCms signedMessage = new SignedCms(content, true);
    // deserialize signature
    signedMessage.Decode(signature);
    try
    {
        // check if signature matches data
        // the certificate is also checked
        signedMessage.CheckSignature(false);
        return true;
    }
    catch { return false; }
}

Figure 14 驗證和刪除簽名

byte[] VerifyAndRemoveSignature(byte[] data)
{
    // create SignedCms
    SignedCms signedMessage = new SignedCms();
    // deserialize PKCS #7 byte array
    signedMessage.Decode(data);
    // check signature
    // false checks signature and certificate
    // true only checks signature
    signedMessage.CheckSignature(false);
    // access signature certificates (if needed)
    foreach (SignerInfo signer in signedMessage.SignerInfos)
    {
        Console.WriteLine("Subject: {0}", 
          signer.Certificate.Subject);
    }
    // return plain data without signature
    return signedMessage.ContentInfo.Content;
}

綜述

在使用安全策略或通信協議時,您會發現各種情況下都需要用到證書。本文介紹了用於檢索和搜索證 書的基礎 API 以及如何利用這些 API 進行加密和數字簽名。此外,我還為大家提供了一些更高級的應用 程序服務的示例,這些服務要求您對 Windows 證書存儲區以及公鑰和私鑰之間的關系有所了解。

本文的源代碼可以從《MSDN雜志》網站下載,其中包括一個很小的支持對文件簽名和加密的 Windows 窗體應用程序。該程序使用了本文所討論的多種方法,如從不同的存儲區選擇證書以及使用加密和簽名來 保護/驗證數據。

本文配套源碼:http://www.bianceng.net/dotnet/201212/790.htm

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved