ASP.NET Core 中的金鑰管理

資料保護系統會自動管理用來保護和解除保護承載的主要金鑰存留期。 每個索引鍵可以存在於四個階段的其中一個:

  • 已建立 - 金鑰通道中存在金鑰通道中,但尚未啟用。 金鑰不應該用於新的保護作業,直到有足夠的時間,金鑰才有機會傳播到所有耗用此金鑰通道的電腦。

  • 作用中 - 金鑰存在於金鑰環中,而且應該用於所有新的保護作業。

  • 已過期 - 金鑰已執行其自然存留期,不應再用於新的保護作業。

  • 已撤銷 - 金鑰遭到入侵,不得用於新的保護作業。

建立、作用中和過期的金鑰都可以用來解除保護傳入承載。 根據預設,撤銷的金鑰可能無法用來解除保護承載,但應用程式開發人員可以在必要時 覆寫此行為

警告

開發人員可能會想要從金鑰環中刪除金鑰, (例如,從檔案系統中刪除對應的檔案) 。 此時,受金鑰保護的所有資料都永久無法加密,而且沒有緊急覆寫,就像有撤銷的金鑰一樣。 刪除金鑰是真正的破壞性行為,因此資料保護系統不會公開任何第一級 API 來執行這項作業。

預設索引鍵選取

當資料保護系統從備份存放庫讀取金鑰通道時,它會嘗試從金鑰通道找出「預設」金鑰。 預設金鑰用於新的保護作業。

一般啟發學習法是資料保護系統選擇最近啟用日期為預設金鑰的金鑰。 (有一個小模糊因素可允許伺服器對伺服器時鐘扭曲。) 如果金鑰已過期或撤銷,且應用程式尚未停用自動金鑰產生,則會根據金鑰 到期和滾動 原則產生立即啟用新的金鑰。

資料保護系統立即產生新的金鑰,而不是回復到不同的金鑰的原因是,新金鑰產生應該視為新金鑰之前啟用的所有金鑰的隱含到期日。 一般概念是,新金鑰可能已使用不同于舊金鑰的不同演算法或待用加密機制進行設定,而且系統應該偏好目前的設定而不是回復。

發生例外狀況。 如果應用程式開發人員 已停用自動產生金鑰,則資料保護系統必須選擇做為預設金鑰的專案。 在此後援案例中,系統會選擇具有最近啟用日期的非撤銷金鑰,並喜好設定為有時間傳播至叢集中其他電腦的金鑰。 後援系統最終可能會選擇過期的預設金鑰作為結果。 後援系統永遠不會選擇撤銷的金鑰做為預設金鑰,如果金鑰通道是空的,或已撤銷每個金鑰,系統就會在初始化時產生錯誤。

金鑰到期和輪流

建立金鑰時,會自動提供 { now + 2 天 } 的啟用日期,以及 { now + 90 天的到期日 }。 啟用之前的 2 天延遲會提供透過系統傳播的關鍵時間。 也就是說,它可讓指向備份存放區的其他應用程式在其下一個自動重新整理期間觀察金鑰,進而將金鑰通道變成作用中的機會最大化,它會傳播到可能需要使用它的所有應用程式。

如果預設金鑰將在 2 天內到期,且金鑰通道尚未在預設金鑰到期時使用中金鑰,則資料保護系統會自動將新金鑰保存到金鑰通道。 這個新金鑰的啟用日期為 { 預設金鑰的到期日 } 和 {now + 90 天的到期日 }。 這可讓系統定期自動復原金鑰,且不會中斷服務。

在某些情況下,將會使用立即啟用來建立金鑰。 其中一個範例是應用程式一段時間未執行,且金鑰通道中的所有金鑰都已過期。 發生這種情況時,金鑰會提供 { now } 的啟用日期,而不會有一般的 2 天啟用延遲。

預設金鑰存留期為 90 天,但如下列範例所示。

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

系統管理員也可以變更整個系統的預設,不過明確呼叫 SetDefaultKeyLifetime 會覆寫任何全系統原則。 預設金鑰存留期不能少於 7 天。

自動按鍵環重新整理

當資料保護系統初始化時,它會從基礎存放庫讀取金鑰通道,並將它快取在記憶體中。 此快取可讓保護與取消保護作業繼續執行,而不需要按下備份存放區。 系統會每隔 24 小時自動檢查備份存放區是否有大約每 24 小時變更,或當目前的預設金鑰到期時,無論何時先發生。

警告

如果) 需要直接使用金鑰管理 API,開發人員應該很少 (。 資料保護系統將會執行自動金鑰管理,如上所述。

資料保護系統會公開介面 IKeyManager ,可用來檢查金鑰環並進行變更。 提供的 實例 IDataProtectionProvider 的 DI 系統也可以提供 的 IKeyManager 實例以供取用。 或者,您也可以從 直接提取 IServiceProviderIKeyManager 如下列範例所示。

任何修改金鑰通道 (明確建立新金鑰或執行撤銷) 的作業都會使記憶體內部快取失效。 下一次呼叫 ProtectUnprotect 會導致資料保護系統重新讀取金鑰通道並重新建立快取。

下列範例示範如何使用 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 討論問題中告訴我們。

金鑰儲存體

資料保護系統具有啟發學習法,它會嘗試自動推斷適當的金鑰儲存位置和待用加密機制。 應用程式開發人員也可以設定金鑰持續性機制。 下列檔討論這些機制的內建實作: