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 が公開されます。

  • GetAllElements :IReadOnlyCollection<XElement>

  • 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 により、これらの要素は構成された IXmlEncryptorEncrypt メソッドを介して実行され、プレーンテキスト要素ではなく暗号化された要素が IXmlRepository に保持されます。 メソッドの出力 EncryptEncryptedXmlInfo オブジェクトです。 このオブジェクトは、結果の暗号化された 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.AddSingletonIServiceCollection.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>
 */