ASP.NET Core でのキー管理

データ保護システムでは、ペイロードの保護と保護解除に使用されるマスター キーの有効期間は自動的に管理されます。 各キーは、4 つの段階のいずれかに存在する可能性があります。

  • 作成済み - キーはキー リング内に存在しますが、まだアクティブになっていません。 このキー リングを使用中のすべてのマシンにキーが伝達される機会があるように、十分な時間が経過するまで、新しい保護操作のためにキーを使用しないようにしてください。

  • アクティブ - キーはキー リング内に存在し、すべての新しい保護操作に使用する必要があります。

  • 期限切れ - すべての有効期間にわたってキーが実行されたので、新しい保護操作には使用してはいけなくなくなりました。

  • 失効済み - キーは侵害されたので、新しい保護操作には使用してはいけません。

作成済み、アクティブ、期限切れのキーはすべて、受信ペイロードの保護を解除するために使用できます。 既定では、失効済みキーを使用してペイロードの保護を解除できない場合がありますが、必要に応じて、アプリケーション開発者がこの動作をオーバーライドできます。

警告

開発者は、キー リングからキーを削除したくなることもあります (ファイル システムから対応するファイルを削除するなど)。 その時点で、そのキーによって保護されているすべてのデータは完全に解読不能になり、失効済みキーの場合のような緊急のオーバーライドは存在しません。 キーの削除は本当に破壊的な動作であり、その結果として、データ保護システムでは、この操作を実行するための "すばらしい" API は公開されません。

既定のキーの選択

データ保護システムでバッキング リポジトリからキー リングが読み取られると、キー リングで、"既定" のキーの検索が試みられます。 既定のキーは、新しい保護操作に使用されます。

データ保護システムでは、アクティブ化の日付が最新であるキーを既定のキーとして選択することが、一般的なヒューリスティックです。 (サーバー間のクロック スキューを許容するための小さな誤差はあります。) そのキーが有効期限切れであるか失効している場合や、アプリケーションで自動キー生成が無効になっていない場合は、後述のキーの有効期限とローリングのポリシーに従い、即座のアクティブ化によって新しいキーが生成されます。

異なるキーにフォールバックするのではなく、データ保護システムによって新しいキーがすぐに生成されるのは、新しいキーの生成は、新しいキーの前にアクティブになったすべてのキーが暗黙的に有効期限を迎える操作として扱う必要があることが理由です。 一般的には、新しいキーは古いキーとは異なるアルゴリズムや保存時の暗号化メカニズムを使用して構成されている可能性があると考えられるため、システムではフォールバックよりも現在の構成を優先させる必要があります。

例外があります。 アプリケーション開発者が自動キー生成を無効にした場合、データ保護システムでは、既定のキーとして何かが選択される必要があります。 このフォールバック シナリオでは、システムはクラスター内の他のマシンに伝達する時間があったキーを優先して、アクティブ化の日付が最新で失効していないキーが選択されます。 フォールバック システムでは、結果として有効期限が切れた既定のキーが選択されることになる可能性があります。 フォールバック システムでは、失効済みのキーが既定のキーとして選択されることはなく、キー リングが空であったりすべてのキーが失効済みであった場合は、初期化時にシステムでエラーが発生します。

キーの有効期限とローリング

キーが作成されるときには、アクティブ化の日付 (現在に 2 日を加える) と有効期限日 (現在に 90 日を加える) が自動的に指定されます。 アクティブ化の前に設ける 2 日間の遅延により、システムを通過して伝達されるための時間がキーに与えられます。 つまり、そのバッキング ストアを参照している他のアプリケーションで、次の自動更新期間にそのキーを確認できるようになります。これで、キー リングがアクティブになったときに、キーを使用する必要がある可能性のあるすべてのアプリケーションにキーが伝達される可能性が最大になります。

既定のキーが 2 日以内に期限切れになる場合や、既定のキーの有効期限が切れたときにアクティブになるキーがキー リングにまだない場合は、データ保護システムにより、新しいキーがキー リングに自動的に保持されます。 この新しいキーのアクティブ化の日付は、既定のキーの有効期限日であり、有効期限日は、現在に 90 日を加えた日です。 これによってシステムでは、サービスを中断することなく、キーの定期的なローリングを自動的に行えます。

即座のアクティブ化によってキーが作成される状況が存在する可能性もあります。 1 つの例は、アプリケーションが一度も実行されず、キー リング内のすべてのキーの有効期限切れになる場合です。 これが発生すると、通常の 2 日間のアクティブ化遅延なしで、現在の日付が、キーに指定されるアクティブ化の日付になります。

既定のキーの有効期間は 90 日ですが、これは次の例のように構成することができます。

services.AddDataProtection()
       // use 14-day lifetime instead of 90-day lifetime
       .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

管理者は、システム全体のポリシーをオーバーライドする SetDefaultKeyLifetime を明示的に呼び出して、システム全体で既定値を変更することもできます。 既定のキーの有効期間は、7 日よりも短くはできません。

キー リングの自動更新

データ保護システムが初期化されるときには、基になるリポジトリからキー リングが読み取られて、メモリにキャッシュされます。 このキャッシュを使用すると、バッキング ストアにアクセスすることなく保護と保護解除の操作を続行できます。 システムでは、約 24 時間ごと、または現在の既定のキーの有効期限が切れるときのいずれか先の時点で、変更がないかバッキング ストアを自動的にチェックします。

警告

開発者がキー管理 API を直接使用する必要がある機会は、仮にあったとしても、ごくまれであるはずです。 データ保護システムでは、前述のように自動キー管理が実行されます。

データ保護システムでは、キー リングを検査して変更を加えるために使用できる IKeyManager インターフェイスが公開されています。 IDataProtectionProvider のインスタンスを提供した DI システムでは、開発者の使用のために IKeyManager のインスタンスも提供できます。 または、下の例のように IServiceProvider から IKeyManager を直接プルできます。

キー リングを変更する (明示的に新しいキーを作成する、失効を実行する) どの操作でも、メモリ内のキャッシュは無効になります。 次に Protect または Unprotect を呼び出すと、データ保護システムによってキー リングが再度読み込まれ、キャッシュが再作成されます。

下記のサンプルでは、既存のキーの取り消しや新しいキーの手動生成など、IKeyManager インターフェイスを使用してキー リングの検査と操作を行う方法を示しています。

using System;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection()
            // point at a specific folder and use DPAPI to encrypt keys
            .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
            .ProtectKeysWithDpapi();
        var services = serviceCollection.BuildServiceProvider();

        // perform a protect operation to force the system to put at least
        // one key in the key ring
        services.GetDataProtector("Sample.KeyManager.v1").Protect("payload");
        Console.WriteLine("Performed a protect operation.");
        Thread.Sleep(2000);

        // get a reference to the key manager
        var keyManager = services.GetService<IKeyManager>();

        // list all keys in the key ring
        var allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }

        // revoke all keys in the key ring
        keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here.");
        Console.WriteLine("Revoked all existing keys.");

        // add a new key to the key ring with immediate activation and a 1-month expiration
        keyManager.CreateNewKey(
            activationDate: DateTimeOffset.Now,
            expirationDate: DateTimeOffset.Now.AddMonths(1));
        Console.WriteLine("Added a new key.");

        // list all keys in the key ring
        allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Performed a protect operation.
 * The key ring contains 1 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = False
 * Revoked all existing keys.
 * Added a new key.
 * The key ring contains 2 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = True
 * Key {2266fc40-e2fb-48c6-8ce2-5fde6b1493f7}: Created = 2015-03-18 22:20:51Z, IsRevoked = False
 */

コードのコメントを英語以外の言語に翻訳し表示したい場合、こちらの GitHub ディスカッション イシューにてお知らせください。

キー記憶域

データ保護システムには、適切なキー ストレージの場所と保存時の暗号化メカニズムの自動推測を試みるためのヒューリスティックが備わっています。 アプリ開発者は、キー永続化メカニズムを構成することもできます。 以下のドキュメントでは、これらのメカニズムの付属する実装について説明します。