Udostępnij za pośrednictwem


Rozszerzalność zarządzania kluczami w programie ASP.NET Core

Zapoznaj się z sekcją zarządzania kluczami przed przeczytaniem tej sekcji, ponieważ objaśniono niektóre podstawowe pojęcia dotyczące tych interfejsów API.

Ostrzeżenie: Typy implementujące dowolny z następujących interfejsów powinny być bezpieczne wątkowo dla wielu wywołujących.

Klucz

Interfejs IKey jest podstawową reprezentacją klucza w systemie kryptograficznym. Termin klucz jest używany tutaj w sensie abstrakcyjnym, a nie w sensie literału "materiału klucza kryptograficznego". Klucz ma następujące właściwości:

  • Daty aktywacji, tworzenia i wygaśnięcia

  • Stan odwołania

  • Identyfikator klucza (identyfikator GUID)

Ponadto uwidacznia metodęCreateEncryptor, IKey która może służyć do tworzenia wystąpienia IAuthenticatedEncryptor powiązanego z tym kluczem.

Ponadto uwidacznia metodęCreateEncryptorInstance, IKey która może służyć do tworzenia wystąpienia IAuthenticatedEncryptor powiązanego z tym kluczem.

Uwaga

Nie ma interfejsu API do pobierania nieprzetworzonego IKey materiału kryptograficznego z wystąpienia.

IKeyManager

Interfejs IKeyManager reprezentuje obiekt odpowiedzialny za ogólny magazyn kluczy, pobieranie i manipulowanie. Uwidacznia trzy operacje wysokiego poziomu:

  • Utwórz nowy klucz i utrwali go w magazynie.

  • Pobierz wszystkie klucze z magazynu.

  • Odwoływanie co najmniej jednego klucza i utrwalanie informacji odwołania do magazynu.

Ostrzeżenie

Pisanie elementu IKeyManager to bardzo zaawansowane zadanie, a większość deweloperów nie powinna jej próbować. Zamiast tego większość deweloperów powinna korzystać z obiektów oferowanych przez klasę XmlKeyManager .

XmlKeyManager

Typ to wbudowana XmlKeyManager konkretna implementacja IKeyManager. Zapewnia kilka przydatnych funkcji, w tym deponowanie kluczy i szyfrowanie kluczy w lokalizacji rest. Klucze w tym systemie są reprezentowane jako elementy XML (w szczególności XElement).

XmlKeyManager zależy od kilku innych składników w trakcie wykonywania zadań:

  • AlgorithmConfiguration, który określa algorytmy używane przez nowe klucze.

  • IXmlRepository, który kontroluje, gdzie klucze są utrwalane w magazynie.

  • IXmlEncryptor [opcjonalnie], który umożliwia szyfrowanie kluczy w lokalizacji rest.

  • IKeyEscrowSink [opcjonalnie], który zapewnia usługi deponowania kluczy.

  • IXmlRepository, który kontroluje, gdzie klucze są utrwalane w magazynie.

  • IXmlEncryptor [opcjonalnie], który umożliwia szyfrowanie kluczy w lokalizacji rest.

  • IKeyEscrowSink [opcjonalnie], który zapewnia usługi deponowania kluczy.

Poniżej przedstawiono diagramy wysokiego poziomu, które wskazują, w jaki sposób te składniki są połączone w obrębie programu XmlKeyManager.

Tworzenie klucza

Tworzenie klucza / TworzenieNowy klucz

W implementacji składnika jest używany do tworzenia unikatowego CreateNewKeyAlgorithmConfiguration IAuthenticatedEncryptorDescriptorelementu , który jest następnie serializowany jako XML. Jeśli istnieje ujście depozytu klucza, nieprzetworzony (niezaszyfrowany) kod XML jest dostarczany do ujścia na potrzeby długoterminowego przechowywania. Niezaszyfrowany kod XML jest następnie uruchamiany za pośrednictwem IXmlEncryptor (jeśli jest to wymagane) w celu wygenerowania zaszyfrowanego dokumentu XML. Ten zaszyfrowany dokument jest utrwalany w długoterminowym magazynie za pośrednictwem elementu IXmlRepository. (Jeśli nie IXmlEncryptor jest skonfigurowany, niezaszyfrowany dokument jest utrwalany w pliku IXmlRepository.)

Pobieranie klucza

Tworzenie klucza

Tworzenie klucza / TworzenieNowy klucz

W implementacji składnika jest używany do tworzenia unikatowego CreateNewKeyIAuthenticatedEncryptorConfiguration IAuthenticatedEncryptorDescriptorelementu , który jest następnie serializowany jako XML. Jeśli istnieje ujście depozytu klucza, nieprzetworzony (niezaszyfrowany) kod XML jest dostarczany do ujścia na potrzeby długoterminowego przechowywania. Niezaszyfrowany kod XML jest następnie uruchamiany za pośrednictwem IXmlEncryptor (jeśli jest to wymagane) w celu wygenerowania zaszyfrowanego dokumentu XML. Ten zaszyfrowany dokument jest utrwalany w długoterminowym magazynie za pośrednictwem elementu IXmlRepository. (Jeśli nie IXmlEncryptor jest skonfigurowany, niezaszyfrowany dokument jest utrwalany w pliku IXmlRepository.)

Pobieranie klucza

Pobieranie klucza /GetAllKeys

W implementacji GetAllKeyspliku dokumenty XML reprezentujące klucze i odwołania są odczytywane z bazowego IXmlRepositoryelementu . Jeśli te dokumenty są szyfrowane, system automatycznie je odszyfruje. XmlKeyManager tworzy odpowiednie IAuthenticatedEncryptorDescriptorDeserializer wystąpienia w celu deserializacji dokumentów z powrotem do IAuthenticatedEncryptorDescriptor wystąpień, które następnie są opakowane w poszczególne IKey wystąpienia. Ta kolekcja wystąpień IKey jest zwracana do obiektu wywołującego.

Więcej informacji na temat konkretnych elementów XML można znaleźć w dokumencie w formacie magazynu kluczy.

IXmlRepository

Interfejs IXmlRepository reprezentuje typ, który może utrwalać kod XML i pobierać kod XML z magazynu kopii zapasowych. Uwidacznia dwa interfejsy API:

  • GetAllElements :IReadOnlyCollection<XElement>

  • StoreElement(XElement element, string friendlyName)

Implementacje IXmlRepository nie muszą analizować kodu XML przechodzącego przez nie. Powinny traktować dokumenty XML jako nieprzezroczyste i pozwolić wyższym warstwom martwić się o generowanie i analizowanie dokumentów.

Istnieją cztery wbudowane typy betonowe, które implementują IXmlRepository:

Aby uzyskać więcej informacji, zobacz dokument dostawcy magazynu kluczy.

Rejestrowanie niestandardowe IXmlRepository jest odpowiednie w przypadku korzystania z innego magazynu kopii zapasowych (na przykład usługi Azure Table Storage).

Aby zmienić domyślne repozytorium dla całej aplikacji, zarejestruj wystąpienie niestandardowe IXmlRepository :

services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());

IXmlEncryptor

Interfejs IXmlEncryptor reprezentuje typ, który może szyfrować element XML w postaci zwykłego tekstu. Uwidacznia pojedynczy interfejs API:

  • Encrypt(XElement plaintextElement) : EncryptedXmlInfo

Jeśli serializowany IAuthenticatedEncryptorDescriptor zawiera jakiekolwiek elementy oznaczone jako "wymaga szyfrowania", XmlKeyManager uruchomi te elementy za pomocą skonfigurowanej IXmlEncryptorEncrypt metody , a następnie utrwały element zaszyfrowany, a nie element zwykłego tekstu do IXmlRepositoryelementu . Dane wyjściowe Encrypt metody to EncryptedXmlInfo obiekt. Ten obiekt jest otoką zawierającą zarówno wynikowe zaszyfrowane XElement , jak i typ, który reprezentuje IXmlDecryptor element, który może służyć do rozszyfrowania odpowiedniego elementu.

Istnieją cztery wbudowane typy betonowe, które implementują IXmlEncryptor:

Aby uzyskać więcej informacji, zobacz szyfrowanie kluczy w rest dokumencie.

Aby zmienić domyślny mechanizm szyfrowania kluczy wrest całej aplikacji, zarejestruj wystąpienie niestandardowe IXmlEncryptor :

services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());

IXmlDecryptor

Interfejs IXmlDecryptor reprezentuje typ, który wie, jak odszyfrować XElement element, który został zaszyfrowany za pomocą elementu IXmlEncryptor. Uwidacznia pojedynczy interfejs API:

  • Decrypt(XElement encryptedElement) : XElement

Metoda Decrypt cofa szyfrowanie wykonywane przez IXmlEncryptor.Encryptprogram . Ogólnie rzecz biorąc, każda konkretna IXmlEncryptor implementacja będzie miała odpowiednią konkretną IXmlDecryptor implementację.

Typy, które implementują IXmlDecryptor , powinny mieć jeden z następujących dwóch konstruktorów publicznych:

  • .ctor(IServiceProvider)
  • .ctor()

Uwaga

Przekazany IServiceProvider do konstruktora może mieć wartość null.

IKeyEscrowSink

Interfejs IKeyEscrowSink reprezentuje typ, który może wykonywać deponowanie poufnych informacji. Pamiętaj, że serializowane deskryptory mogą zawierać poufne informacje (takie jak materiał kryptograficzny), co doprowadziło do wprowadzenia typu IXmlEncryptor w pierwszej kolejności. Jednak zdarzają się wypadki, a pierścienie kluczy można usunąć lub ulec uszkodzeniu.

Interfejs depozytu zapewnia awaryjne wyjście ucieczki, umożliwiając dostęp do nieprzetworzonego serializowanego kodu XML przed przekształceniem przez dowolny skonfigurowany program IXmlEncryptor. Interfejs uwidacznia jeden interfejs API:

  • Store(Guid keyId, XElement, element)

IKeyEscrowSink Do implementacji należy obsługa dostarczonego elementu w bezpieczny sposób zgodny z zasadami biznesowymi. Jedną z możliwych implementacji może być ujście depozytu w celu zaszyfrowania elementu XML przy użyciu znanego firmowego certyfikatu X.509, w którym klucz prywatny certyfikatu został deponowany; typ CertificateXmlEncryptor może pomóc w tym. Implementacja IKeyEscrowSink jest również odpowiedzialna za odpowiednie utrwalanie dostarczonego elementu.

Domyślnie żaden mechanizm deponowania nie jest włączony, chociaż administratorzy serwerów mogą skonfigurować tę funkcję globalnie. Można go również skonfigurować programowo za pomocą IDataProtectionBuilder.AddKeyEscrowSink metody, jak pokazano w poniższym przykładzie. Metoda AddKeyEscrowSink przeciąża dublowanie IServiceCollection.AddSingleton przeciążeń i IServiceCollection.AddInstance , ponieważ IKeyEscrowSink wystąpienia mają być pojedyncze. Jeśli zarejestrowano wiele IKeyEscrowSink wystąpień, każdy z nich będzie wywoływany podczas generowania klucza, dzięki czemu klucze mogą być deponowane do wielu mechanizmów jednocześnie.

Nie ma interfejsu API do odczytywania materiału z IKeyEscrowSink wystąpienia. Jest to zgodne z teorią projektowania mechanizmu deponowania: ma na celu udostępnienie kluczowego materiału zaufanemu urzędowi, a ponieważ aplikacja nie jest zaufanym urzędem, nie powinna mieć dostępu do własnego materiału deponowanego.

Poniższy przykładowy kod demonstruje tworzenie i rejestrowanie IKeyEscrowSink , w którym klucze są deponowane, tak aby tylko członkowie "ADMINISTRATORZy CONTOSODomain" mogli je odzyskać.

Uwaga

Aby uruchomić ten przykład, musisz być na komputerze z systemem Windows 8 / Windows Server 2012 przyłączonym do domeny, a kontroler domeny musi być systemem Windows Server 2012 lub nowszym.

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>
 */