Extensibilidad de administración de claves en ASP.NET Core
Lea la sección administración de claves antes de leer esta sección, ya que explica algunos de los conceptos fundamentales de estas API.
Advertencia: los tipos que implementan cualquiera de las siguientes interfaces deben ser seguros para múltiples llamadas.
Clave
La interfaz IKey
es la representación básica de una clave en un sistema criptográfico. El término clave se usa aquí en el sentido abstracto, no en el sentido literal de "material de clave criptográfica". Una clave tiene las siguientes propiedades:
Fechas de activación, creación y expiración
Estado de revocación
Identificador de clave (un GUID)
Además, IKey
expone un método CreateEncryptor
que puede usarse para crear una instancia de IAuthenticatedEncryptor vinculada a esta clave.
Además, IKey
expone un método CreateEncryptorInstance
que puede usarse para crear una instancia de IAuthenticatedEncryptor vinculada a esta clave.
Nota
No hay ninguna API para recuperar el material criptográfico sin procesar de una instancia de IKey
.
IKeyManager
La interfaz de IKeyManager
representa un objeto responsable del almacenamiento, la recuperación y la manipulación de claves en general. Expone tres operaciones de alto nivel:
Crear una nueva clave y conservarla en el almacenamiento.
Obtener todas las claves del almacenamiento.
Revocar una o varias claves y conservar la información de la revocación en el almacenamiento.
Advertencia
Escribir un IKeyManager
es una tarea muy avanzada y la mayoría de los desarrolladores no deben intentarlo. En su lugar, la mayoría de los desarrolladores deben aprovechar las ventajas que ofrece la clase XmlKeyManager .
XmlKeyManager
El tipo XmlKeyManager
es la implementación concreta integrada de IKeyManager
. Proporciona varias funciones útiles, como la custodia de claves y el cifrado de claves en rest. Las claves de este sistema se representan como elementos XML (en concreto, XElement).
XmlKeyManager
depende de otros componentes en el curso de cumplir sus tareas:
AlgorithmConfiguration
, que dicta los algoritmos usados por nuevas claves.IXmlRepository
, que controla dónde se conservan las claves en el almacenamiento.IXmlEncryptor
[opcional], que permite cifrar claves en rest.IKeyEscrowSink
[opcional], que proporciona servicios de custodia de claves.
IXmlRepository
, que controla dónde se conservan las claves en el almacenamiento.IXmlEncryptor
[opcional], que permite cifrar claves en rest.IKeyEscrowSink
[opcional], que proporciona servicios de custodia de claves.
A continuación se muestran diagramas de alto nivel que indican cómo estos componentes están conectados juntos dentro de XmlKeyManager
.
Creación de claves/CreateNewKey
En la implementación de CreateNewKey
, el componente AlgorithmConfiguration
se usa para crear un IAuthenticatedEncryptorDescriptor
único, que después se serializa como XML. Si existe un receptor de custodia de claves, el XML sin cifrar se proporciona al receptor para el almacenamiento a largo plazo. El XML sin cifrar se pasa después por un IXmlEncryptor
(si es necesario) para generar el documento XML cifrado. Este documento cifrado se guarda a largo plazo a través del IXmlRepository
. (Si no se configura el IXmlEncryptor
, el documento sin cifrar se mantiene en el IXmlRepository
.)
Creación de claves/CreateNewKey
En la implementación de CreateNewKey
, el componente IAuthenticatedEncryptorConfiguration
se usa para crear un IAuthenticatedEncryptorDescriptor
único, que después se serializa como XML. Si existe un receptor de custodia de claves, el XML sin cifrar se proporciona al receptor para el almacenamiento a largo plazo. El XML sin cifrar se pasa después por un IXmlEncryptor
(si es necesario) para generar el documento XML cifrado. Este documento cifrado se guarda a largo plazo a través del IXmlRepository
. (Si no se configura el IXmlEncryptor
, el documento sin cifrar se mantiene en el IXmlRepository
.)
Recuperación de claves / GetAllKeys
En la implementación de GetAllKeys
, los documentos XML que representan claves y revocaciones se leen del IXmlRepository
subyacente. Si estos documentos están cifrados, el sistema los descifrará automáticamente. XmlKeyManager
crea las instancias de IAuthenticatedEncryptorDescriptorDeserializer
apropiadas para deserializar los documentos de nuevo en instancias de IAuthenticatedEncryptorDescriptor
, que después se envuelven en instancias individuales de IKey
. Esta colección de instancias de IKey
se devuelve a la persona que llama.
Encontrará más información sobre los elementos XML concretos en el documento de formato de almacenamiento de claves.
IXmlRepository
La interfaz de IXmlRepository
representa un tipo que puede hacer persistir XML en un almacén de respaldo y recoger XML de él. Expone dos API:
GetAllElements
:IReadOnlyCollection<XElement>
StoreElement(XElement element, string friendlyName)
Las implementaciones de IXmlRepository
no necesitan analizar el XML que pasa a través de ellas. Deben tratar los documentos XML como opacos y dejar que las capas superiores se preocupen por generar y analizar los documentos.
Hay cuatro tipos concretos integrados que implementan IXmlRepository
:
Consulte el documento de proveedores de almacenamiento de claves para más información.
El registro de un IXmlRepository
personalizado es apropiado cuando se usa un almacén de respaldo diferente (por ejemplo, Azure Table Storage).
Para cambiar el repositorio predeterminado en toda la aplicación, registre una instancia personalizada de IXmlRepository
:
services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());
IXmlEncryptor
La interfaz de IXmlEncryptor
representa un tipo que puede cifrar un elemento XML en texto sin formato. Expone una sola API:
- Encrypt(XElement plaintextElement) : EncryptedXmlInfo
Si un IAuthenticatedEncryptorDescriptor
serializado contiene cualquier elemento marcado como "requiere encriptación", entonces XmlKeyManager
ejecutará esos elementos a través del método Encrypt
del IXmlEncryptor
configurado, y conservará el elemento cifrado en lugar del elemento de texto sin formato en el IXmlRepository
. La salida del método Encrypt
es un objeto EncryptedXmlInfo
. Este objeto es un contenedor que contiene tanto el XElement
cifrado resultante como el Tipo que representa un IXmlDecryptor
que puede usarse para descifrar el elemento correspondiente.
Hay cuatro tipos concretos integrados que implementan IXmlEncryptor
:
Consulta el documento de cifrado de claves en rest para más información.
Para cambiar el mecanismo predeterminado de cifrado de claves en rest en toda la aplicación, registra una instancia de IXmlEncryptor
personalizada:
services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());
IXmlDecryptor
La interfaz de IXmlDecryptor
representa un tipo que sabe descifrar un XElement
que se ha cifrado a través de un IXmlEncryptor
. Expone una sola API:
- Decrypt(XElement encryptedElement) : XElement
El método Decrypt
deshace el cifrado realizado por IXmlEncryptor.Encrypt
. En general, cada implementación concreta de IXmlEncryptor
tendrá su correspondiente implementación concreta de IXmlDecryptor
.
Los tipos que implementan IXmlDecryptor
deben tener uno de los dos constructores públicos siguientes:
- .ctor(IServiceProvider)
- .ctor()
Nota:
El IServiceProvider
pasado al constructor puede ser null.
IKeyEscrowSink
La interfaz de IKeyEscrowSink
representa un tipo que puede realizar la custodia de información confidencial. Recordemos que los descriptores serializados pueden contener información confidencial (como material criptográfico), y esto es lo que llevó a la introducción del tipo IXmlEncryptor en primer lugar. Sin embargo, los accidentes ocurren y los llaveros pueden eliminarse o dañarse.
La interfaz de custodia proporciona una puerta de escape de emergencia, permitiendo el acceso al XML serializado en bruto antes de que sea transformado por cualquier IXmlEncryptor configurado. La interfaz expone una sola API:
- Store(Guid keyId, elemento XElement)Store(Guid keyId, XElement element)
Depende de la implementación de IKeyEscrowSink
controlar el elemento proporcionado de forma segura y coherente con la directiva empresarial. Una posible implementación podría ser que el receptor de custodia cifrara el elemento XML usando un certificado X.509 corporativo conocido en el que la clave privada del certificado haya sido custodiada; el tipo CertificateXmlEncryptor
puede ayudar con esto. La implementación de IKeyEscrowSink
también es responsable de conservar adecuadamente el elemento proporcionado.
De forma predeterminada, no se habilita ningún mecanismo de custodia, aunque los administradores del servidor pueden configurar esto globalmente. También puede configurarse mediante programación a través del método IDataProtectionBuilder.AddKeyEscrowSink
, como se muestra en el siguiente ejemplo. Las sobrecargas del método AddKeyEscrowSink
reflejan las sobrecargas de IServiceCollection.AddSingleton
y IServiceCollection.AddInstance
, ya que se pretende que las instancias de IKeyEscrowSink
sean singletons. Si se registran varias instancias de IKeyEscrowSink
, se llamará a cada una de ellas durante la generación de claves, por lo que se pueden depositar claves en varios mecanismos simultáneamente.
No existe una API para leer material de una instancia de IKeyEscrowSink
. Esto es coherente con la teoría de diseño del mecanismo de custodia: está pensado para que el material clave sea accesible para una autoridad de confianza y, dado que la aplicación no es una autoridad de confianza, no debe tener acceso a su propio material permitido.
En el código de ejemplo siguiente se muestra cómo crear y registrar un IKeyEscrowSink
donde se permiten las claves, de modo que solo los miembros de "CONTOSODomain Admins" pueden recuperarlas.
Nota:
Para ejecutar este ejemplo, debe estar en una máquina Windows 8 o Windows Server 2012 unida a un dominio y el controlador de dominio debe ser Windows Server 2012 o posterior.
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>
*/