ASP.NET Core のキー管理機能拡張
このセクションを読む前に、キー管理に関するセクションをお読みください。そこでは、これらの API の基本的な概念の一部について説明しています。
警告: 次のいずれかのインターフェイスを実装する型は、複数の呼び出し元に対してスレッドセーフである必要があります。
キー
IKey
インターフェイスは、暗号システム内のキーの基本的な表現です。 ここでは、文字どおりの "暗号化キー マテリアル" の意味ではなく、抽象的な意味でキーという用語を使用します。 キーには次の特性があります。
アクティブ化、作成、有効期限
失効状態
キー識別子 (GUID)
さらに、IKey
により、このキーに関連付けられた IAuthenticatedEncryptor インスタンスを作成するために使用できる CreateEncryptor
メソッドが公開されます。
さらに、IKey
により、このキーに関連付けられた IAuthenticatedEncryptor インスタンスを作成するために使用できる CreateEncryptorInstance
メソッドが公開されます。
Note
IKey
インスタンスから未加工の暗号化マテリアルを取得する API はありません。
IKeyManager
IKeyManager
インターフェイスは、一般的なキーの格納、取得、操作を行うオブジェクトを表します。 これにより、3 つの高レベルの操作が公開されます。
新しいキーを作成し、ストレージに保持します。
ストレージからすべてのキーを取得します。
1 つ以上のキーを失効させ、失効情報をストレージに保持します。
警告
IKeyManager
の記述はとても高度なタスクであり、ほとんどの開発者はそれを試みるべきではありません。 代わりに、ほとんどの開発者は、XmlKeyManager クラスによって提供される機能を利用する必要があります。
XmlKeyManager
XmlKeyManager
型は、IKeyManager
のインボックスの具象実装です。 これには、キー エスクローやrestのキーの暗号化など、便利な機能がいくつか用意されています。 このシステムのキーは、XML 要素 (具体的には XElement) として表されます。
XmlKeyManager
は、タスクを実行する過程で、他のいくつかのコンポーネントに依存します。
AlgorithmConfiguration
。新しいキーによって使用されるアルゴリズムを指定します。IXmlRepository
。ストレージ内でキーが保持される場所を制御します。IXmlEncryptor
[省略可能]。 restのキーの暗号化を許可します。IKeyEscrowSink
[省略可能]。キー エスクロー サービスを提供します。
IXmlRepository
。ストレージ内でキーが保持される場所を制御します。IXmlEncryptor
[省略可能]。 restのキーの暗号化を許可します。IKeyEscrowSink
[省略可能]。キー エスクロー サービスを提供します。
これらのコンポーネントが XmlKeyManager
内でどのように結び付けられるかを示す概略図を次に示します。
キーの作成/CreateNewKey
CreateNewKey
の実装では、AlgorithmConfiguration
コンポーネントを使用して一意の IAuthenticatedEncryptorDescriptor
を作成します。これは、XML としてシリアル化されます。 キー エスクロー シンクが存在する場合は、未加工の (暗号化されていない) XML が、長期保存のためにシンクに提供されます。 暗号化されていない XML は、暗号化された XML ドキュメントを生成するために、(必要に応じて) IXmlEncryptor
を介して実行されます。 この暗号化されたドキュメントは、IXmlRepository
を介して長期的なストレージに保持されます。 (IXmlEncryptor
が構成されていない場合、暗号化されていないドキュメントは IXmlRepository
に保持されます。)
キーの作成/CreateNewKey
CreateNewKey
の実装では、IAuthenticatedEncryptorConfiguration
コンポーネントを使用して一意の IAuthenticatedEncryptorDescriptor
を作成します。これは、XML としてシリアル化されます。 キー エスクロー シンクが存在する場合は、未加工の (暗号化されていない) XML が、長期保存のためにシンクに提供されます。 暗号化されていない XML は、暗号化された XML ドキュメントを生成するために、(必要に応じて) IXmlEncryptor
を介して実行されます。 この暗号化されたドキュメントは、IXmlRepository
を介して長期的なストレージに保持されます。 (IXmlEncryptor
が構成されていない場合、暗号化されていないドキュメントは IXmlRepository
に保持されます。)
キーの取得/GetAllKeys
GetAllKeys
の実装では、キーと失効を表す XML ドキュメントが、基になる IXmlRepository
から読み取られます。 これらのドキュメントが暗号化されている場合は、システムによって自動的に暗号化が解除されます。 XmlKeyManager
により、ドキュメントを IAuthenticatedEncryptorDescriptor
インスタンスに逆シリアル化する適切な IAuthenticatedEncryptorDescriptorDeserializer
インスタンスが作成され、個々の IKey
インスタンスにラップされます。 この IKey
インスタンスのコレクションが呼び出し元に返されます。
特定の XML 要素の詳細については、キー ストレージ形式に関するドキュメントを参照してください。
IXmlRepository
IXmlRepository
インターフェイスは、バッキング ストアとの間で XML の永続化や XML の取得を行える型を表します。 2 つの API が公開されます。
$
StoreElement(XElement element, string friendlyName)
IXmlRepository
の実装では、それらを渡す XML を解析する必要はありません。 これらは XML ドキュメントを不透明として扱い、ドキュメントの生成と解析についてはより高いレイヤーで考慮するようにします。
IXmlRepository
を実装する組み込みの具象型は 4 つあります。
詳細については、キー ストレージ プロバイダーに関するドキュメントをご覧ください。
カスタム IXmlRepository
の登録は、別のバッキング ストア (Azure Table Storage など) を使用する場合に適しています。
既定のリポジトリ アプリケーション全体を変更するには、カスタム IXmlRepository
インスタンスを登録します。
services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());
IXmlEncryptor
IXmlEncryptor
インターフェイスは、プレーンテキストの XML 要素を暗号化できる型を表します。 これにより、1 つの API が公開されます。
- Encrypt(XElement plaintextElement) : EncryptedXmlInfo
シリアル化された IAuthenticatedEncryptorDescriptor
に "暗号化が必要" とマークされた要素が含まれている場合、XmlKeyManager
により、これらの要素は構成された IXmlEncryptor
の Encrypt
メソッドを介して実行され、プレーンテキスト要素ではなく暗号化された要素が IXmlRepository
に保持されます。 メソッドの出力 Encrypt
は EncryptedXmlInfo
オブジェクトです。 このオブジェクトは、結果の暗号化された XElement
と、対応する要素を解読するために使用できる IXmlDecryptor
を表す型の両方を含むラッパーです。
IXmlEncryptor
を実装する組み込みの具象型は 4 つあります。
詳細については、restのキー暗号化に関するドキュメントをご覧ください。
既定の rest 時のキー暗号化メカニズムをアプリケーション全体で変更するには、カスタム IXmlEncryptor
インスタンスを登録します。
services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());
IXmlDecryptor
IXmlDecryptor
インターフェイスは、IXmlEncryptor
を介して暗号化された XElement
を暗号化解除する方法を認識している型を表します。 これにより、1 つの API が公開されます。
- Decrypt(XElement encryptedElement) : XElement
Decrypt
メソッドでは、IXmlEncryptor.Encrypt
によって実行された暗号化が元に戻されます。 一般に、各具象 IXmlEncryptor
実装には、対応する具象 IXmlDecryptor
実装があります。
IXmlDecryptor
を実装する型には、次の 2 つのパブリック コンストラクターのいずれかが必要です。
- .ctor(IServiceProvider)
- .ctor()
Note
コンストラクターに渡される IServiceProvider
は null の場合があります。
IKeyEscrowSink
IKeyEscrowSink
インターフェイスは、機密情報のエスクローを実行できる型を表します。 シリアル化された記述子に機密情報 (暗号化マテリアルなど) が含まれている可能性があることを思い出してください。これは、最初の IXmlEncryptor 型の導入につながりました。 しかし、偶発的にキー リングが削除されたり、破損したりする可能性があります。
エスクロー インターフェイスによって緊急エスケープ ハッチが提供されるので、構成済みの IXmlEncryptor によって変換される前に未加工のシリアル化された XML にアクセスできます。 このインターフェイスにより、単一の API が公開されます。
- Store(Guid keyId, XElement element)
ビジネス ポリシーと一貫性のある安全な方法で提供される要素の処理は、IKeyEscrowSink
実装によって行います。 実装の 1 つとして、エスクロー シンクにより、証明書の秘密キーのエスクローが行われた既知の企業の X.509 証明書を使用して XML 要素を暗号化することが考えられます。CertificateXmlEncryptor
型でこれを支援できます。 IKeyEscrowSink
実装は、指定された要素を適切に保持する役割も担います。
既定では、エスクロー メカニズムは有効になっていませんが、サーバー管理者はこれをグローバルに構成することができます。 次のサンプルに示すように、IDataProtectionBuilder.AddKeyEscrowSink
メソッドを使用してプログラムで構成することもできます。 IKeyEscrowSink
インスタンスはシングルトンとして使用されるため、AddKeyEscrowSink
メソッド オーバーロードにより IServiceCollection.AddSingleton
と IServiceCollection.AddInstance
のオーバーロードがミラー化されます。 複数の IKeyEscrowSink
インスタンスが登録されている場合は、キーの生成時にそれぞれが呼び出されるため、キーを複数のメカニズムに同時にエスクローすることができます。
IKeyEscrowSink
インスタンスからマテリアルを読み取る API はありません。 これはエスクロー メカニズムの設計理論と一貫性があります。これは、信頼された機関からキー マテリアルにアクセスできるようにするためのものであり、アプリケーション自体は信頼された機関ではないため、独自のエスクロー マテリアルにアクセスできないようにする必要があります。
次のサンプル コードは、"CONTOSODomain Admins" のメンバーによってのみ回復できるようにキーがエスクローされる IKeyEscrowSink
の作成と登録を示しています。
Note
このサンプルを実行するには、ドメインに参加している 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>
*/
ASP.NET Core