閱讀本節之前請先閱讀金鑰管理一節,因為它說明這些 API 背後的一些基本概念。
警告:實作以下任何介面的型別對於多個呼叫端來說應該是安全執行緒。
按鍵
IKey 介面是密碼編譯系統中的金鑰基本表示法。 這裡使用的術語「金鑰」是抽象意義上的,而不是字面上的「密碼編譯金鑰資料」。 金鑰具有下列屬性:
啟用、建立和到期日
撤銷狀態
金鑰識別碼 (GUID)
此外,IKey 會公開 CreateEncryptor 方法,可用來建立繫結至此金鑰的 IAuthenticatedEncryptor 執行個體。
此外,IKey 會公開 CreateEncryptorInstance 方法,可用來建立繫結至此金鑰的 IAuthenticatedEncryptor 執行個體。
注意
沒有 API 可從 IKey 執行個體擷取原始密碼編譯資料。
IKeyManager
IKeyManager 介面代表負責一般金鑰儲存、擷取和操作的物件。 它會公開三個高階作業:
建立新的金鑰,並將它保存至儲存體。
從儲存體取得所有金鑰。
撤銷一或多個金鑰,並將撤銷資訊保存至儲存體。
警告
撰寫 IKeyManager 是一項非常進階的工作,大多數開發人員都不應該嘗試。 相反地,大多數開發人員都應該利用 XmlKeyManager 類別所提供的功能。
XmlKeyManager
XmlKeyManager 型別是 IKeyManager 的內建具體實作。 它提供多項實用功能,包括密鑰託管及靜態密鑰加密。 此系統中的金鑰會以 XML 元素表示 (特別是 XElement)。
XmlKeyManager 在完成其工作的過程中,相依於其他幾個元件:
AlgorithmConfiguration,指定新金鑰所使用的演算法。IXmlRepository,控制金鑰保存在儲存體中的位置。IXmlEncryptor[可選性],允許在靜態時加密密鑰。IKeyEscrowSink[選擇性],提供金鑰委付服務。
IXmlRepository,控制金鑰保存在儲存體中的位置。IXmlEncryptor[可選性],允許在靜態時加密密鑰。IKeyEscrowSink[選擇性],提供金鑰委付服務。
以下是高階圖表,指出這些元件如何在 XmlKeyManager 內連接在一起。
金鑰建立 / CreateNewKey
在 CreateNewKey 的實作中,AlgorithmConfiguration 元件是用來建立唯一的 IAuthenticatedEncryptorDescriptor,然後序列化為 XML。 如果金鑰委付接收器存在,則會將原始的 (未加密) XML 提供給長期儲存體的接收器。 然後,系統會透過 IXmlEncryptor 執行未加密的 XML (若需要),以產生加密的 XML 文件。 此加密文件會透過 IXmlRepository 保存至長期儲存體。 (如果未設定 IXmlEncryptor,則未加密文件會保存在 IXmlRepository 中。)
金鑰建立 / CreateNewKey
在 CreateNewKey 的實作中,IAuthenticatedEncryptorConfiguration 元件是用來建立唯一的 IAuthenticatedEncryptorDescriptor,然後序列化為 XML。 如果金鑰委付接收器存在,則會將原始的 (未加密) XML 提供給長期儲存體的接收器。 然後,系統會透過 IXmlEncryptor 執行未加密的 XML (若需要),以產生加密的 XML 文件。 此加密文件會透過 IXmlRepository 保存至長期儲存體。 (如果未設定 IXmlEncryptor,則未加密文件會保存在 IXmlRepository 中。)
金鑰擷取 / GetAllKeys
在 GetAllKeys 的實作中,系統會從基礎 IXmlRepository 讀取代表金鑰和撤銷的 XML 文件。 如果這些文件已加密,系統會自動解密這些文件。
XmlKeyManager 會建立適當的 IAuthenticatedEncryptorDescriptorDeserializer 執行個體,以將文件還原序列化回 IAuthenticatedEncryptorDescriptor 執行個體,然後包裝在個別 IKey 執行個體中。 這個 IKey 執行個體集合會傳回給呼叫端。
如需特定 XML 元素的詳細資訊,請參閱金鑰儲存體格式文件。
IXmlRepository
IXmlRepository 介面代表可以將 XML 保存到備份存放區並從備份存放區擷取 XML 的型別。 它會公開兩個 API:
%
StoreElement(XElement element, string friendlyName)
IXmlRepository 的實作不需要剖析傳遞它們的 XML。 它們應該將 XML 文件視為不透明,並讓較高層級負責產生和剖析文件。
有四個實作 IXmlRepository 的內建具體型別:
如需詳細資訊,請參閱金鑰儲存體提供者文件。
使用不同的備份存放區時,適合註冊自訂 IXmlRepository (例如,Azure 資料表儲存體)。
若要變更整個應用程式的預設存放庫,請註冊自訂 IXmlRepository 執行個體:
services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());
IXmlEncryptor
IXmlEncryptor 介面代表可以加密純文字 XML 元素的型別。 它會公開單一 API:
- Encrypt(XElement plaintextElement):EncryptedXmlInfo
如果序列化 IAuthenticatedEncryptorDescriptor 包含任何標示為「需要加密」的元素,則 XmlKeyManager 會透過 IXmlEncryptor 設定的 Encrypt 方法執行這些元素,並將加密的元素而非純文字元素保存至 IXmlRepository。
Encrypt 方法的輸出是 EncryptedXmlInfo 物件。 此物件是包裝函式,其中包含產生的加密 XElement 和 Type,其代表可用來解密對應元素的 IXmlDecryptor。
有四個實作 IXmlEncryptor 的內建具體型別:
更多資訊請參閱 靜止金鑰加密文件 。
若要在整個應用程式範圍內更改預設的靜態金鑰加密機制,請註冊一個自訂 IXmlEncryptor 實例:
services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());
IXmlDecryptor
IXmlDecryptor 介面代表一種型別,該型別知道如何解密透過 XElement 加密的 IXmlEncryptor。 它會公開單一 API:
- Decrypt(XElement encryptedElement) : XElement
Decrypt 方法會復原 IXmlEncryptor.Encrypt 所執行的加密。 一般而言,每個具體 IXmlEncryptor 實作都會有對應的具體 IXmlDecryptor 實作。
實作 IXmlDecryptor 的型別應該有下列兩個公用建構函式之一:
- .ctor(IServiceProvider)
- .ctor()
注意
傳遞至建構函式的 IServiceProvider 可能是 Null。
IKeyEscrowSink
IKeyEscrowSink 介面代表可執行敏感性資訊委付的型別。 回想一下,序列化描述項可能包含敏感性資訊 (例如密碼編譯資料),這正是導致引進 IXmlEncryptor 型別的原因。 然而,意外發生時,金鑰環可能會被刪除或損壞。
委付介面提供緊急逸出介面,允許在任何已設定 IXmlEncryptor 的轉換原始序列化 XML 之前,先存取原始序列化 XML。 介面會公開單一 API:
- Store(Guid keyId,XElement element)
這取決於 IKeyEscrowSink 實作的方式,以與商務原則一致的安全方式處理所提供的元素。 其中一個可能的實作是讓委付接收器使用已知的公司 X.509 憑證來加密 XML 元素,其中憑證的私密金鑰已被委付;CertificateXmlEncryptor 型別可協助進行這項處理。
IKeyEscrowSink 實作也會負責適當地保存提供的元素。
根據預設,不會啟用委付機制,不過伺服器管理員可以全域設定此機制。 您也可以透過 IDataProtectionBuilder.AddKeyEscrowSink 方法以程式設計方式進行設定,如下列範例所示。
AddKeyEscrowSink 方法多載會鏡像 IServiceCollection.AddSingleton 和 IServiceCollection.AddInstance 多載,因為 IKeyEscrowSink 執行個體是單一資料庫。 如果註冊多個 IKeyEscrowSink 執行個體,系統會在金鑰產生期間呼叫每個執行個體,因此可以同時將金鑰委付至多個機制。
沒有 API 可從 IKeyEscrowSink 執行個體讀取資料。 這與委付機制的設計理論一致:它的目的是讓信任的授權單位可存取金鑰資料,而且由於應用程式本身不是受信任的授權單位,因此它不應該能夠存取自己的委付資料。
下列範例程式碼示範如何建立和註冊 IKeyEscrowSink,其中金鑰被委付,以便只有「CONTOSODomain 系統管理員」的成員才能恢復它們。
注意
若要執行此範例,您必須使用已加入網域的 Windows 8 / Windows Server 2012 電腦,而且網域控制站必須是 Windows Server 2012 或更新版本。
using System;
using System.IO;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
public class Program
{
public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
.ProtectKeysWithDpapi()
.AddKeyEscrowSink(sp => new MyKeyEscrowSink(sp));
var services = serviceCollection.BuildServiceProvider();
// get a reference to the key manager and force a new key to be generated
Console.WriteLine("Generating new key...");
var keyManager = services.GetService<IKeyManager>();
keyManager.CreateNewKey(
activationDate: DateTimeOffset.Now,
expirationDate: DateTimeOffset.Now.AddDays(7));
}
// A key escrow sink where keys are escrowed such that they
// can be read by members of the CONTOSO\Domain Admins group.
private class MyKeyEscrowSink : IKeyEscrowSink
{
private readonly IXmlEncryptor _escrowEncryptor;
public MyKeyEscrowSink(IServiceProvider services)
{
// Assuming I'm on a machine that's a member of the CONTOSO
// domain, I can use the Domain Admins SID to generate an
// encrypted payload that only they can read. Sample SID from
// https://technet.microsoft.com/library/cc778824(v=ws.10).aspx.
_escrowEncryptor = new DpapiNGXmlEncryptor(
"SID=S-1-5-21-1004336348-1177238915-682003330-512",
DpapiNGProtectionDescriptorFlags.None,
new LoggerFactory());
}
public void Store(Guid keyId, XElement element)
{
// Encrypt the key element to the escrow encryptor.
var encryptedXmlInfo = _escrowEncryptor.Encrypt(element);
// A real implementation would save the escrowed key to a
// write-only file share or some other stable storage, but
// in this sample we'll just write it out to the console.
Console.WriteLine($"Escrowing key {keyId}");
Console.WriteLine(encryptedXmlInfo.EncryptedElement);
// Note: We cannot read the escrowed key material ourselves.
// We need to get a member of CONTOSO\Domain Admins to read
// it for us in the event we need to recover it.
}
}
}
/*
* SAMPLE OUTPUT
*
* Generating new key...
* Escrowing key 38e74534-c1b8-4b43-aea1-79e856a822e5
* <encryptedKey>
* <!-- This key is encrypted with Windows DPAPI-NG. -->
* <!-- Rule: SID=S-1-5-21-1004336348-1177238915-682003330-512 -->
* <value>MIIIfAYJKoZIhvcNAQcDoIIIbTCCCGkCAQ...T5rA4g==</value>
* </encryptedKey>
*/