// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.IO; using Microsoft.AspNet.DataProtection.AuthenticatedEncryption; using Microsoft.AspNet.DataProtection.KeyManagement; using Microsoft.AspNet.DataProtection.XmlEncryption; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Win32; #if !DOTNET5_4 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml using System.Security.Cryptography.X509Certificates; #endif namespace Microsoft.AspNet.DataProtection { /// /// Provides access to configuration for the data protection system, which allows the /// developer to configure default cryptographic algorithms, key storage locations, /// and the mechanism by which keys are protected at rest. /// /// /// /// If the developer changes the at-rest key protection mechanism, it is intended that /// he also change the key storage location, and vice versa. For instance, a call to /// should generally be accompanied by /// a call to , or exceptions may /// occur at runtime due to the data protection system not knowing where to persist keys. /// /// /// Similarly, when a developer modifies the default protected payload cryptographic /// algorithms, it is intended that he also select an explitiy key storage location. /// A call to /// should therefore generally be paired with a call to , /// for example. /// /// /// When the default cryptographic algorithms or at-rest key protection mechanisms are /// changed, they only affect new keys in the repository. The repository may /// contain existing keys that use older algorithms or protection mechanisms. /// /// public class DataProtectionConfiguration { /// /// Creates a new configuration object linked to a . /// public DataProtectionConfiguration(IServiceCollection services) { if (services == null) { throw new ArgumentNullException(nameof(services)); } Services = services; } /// /// Provides access to the passed to this object's constructor. /// [EditorBrowsable(EditorBrowsableState.Never)] public IServiceCollection Services { get; } /// /// Registers a to perform escrow before keys are persisted to storage. /// /// The instance of the to register. /// The 'this' instance. /// /// Registrations are additive. /// public DataProtectionConfiguration AddKeyEscrowSink(IKeyEscrowSink sink) { if (sink == null) { throw new ArgumentNullException(nameof(sink)); } Services.AddSingleton(sink); return this; } /// /// Registers a to perform escrow before keys are persisted to storage. /// /// The concrete type of the to register. /// The 'this' instance. /// /// Registrations are additive. The factory is registered as . /// public DataProtectionConfiguration AddKeyEscrowSink() where TImplementation : class, IKeyEscrowSink { Services.AddSingleton(); return this; } /// /// Registers a to perform escrow before keys are persisted to storage. /// /// A factory that creates the instance. /// The 'this' instance. /// /// Registrations are additive. The factory is registered as . /// public DataProtectionConfiguration AddKeyEscrowSink(Func factory) { if (factory == null) { throw new ArgumentNullException(nameof(factory)); } Services.AddSingleton(factory); return this; } /// /// Configures miscellaneous global options. /// /// A callback that configures the global options. /// The 'this' instance. public DataProtectionConfiguration ConfigureGlobalOptions(Action setupAction) { if (setupAction == null) { throw new ArgumentNullException(nameof(setupAction)); } Services.Configure(setupAction); return this; } /// /// Configures the data protection system not to generate new keys automatically. /// /// The 'this' instance. /// /// Calling this API corresponds to setting /// to 'false'. See that property's documentation for more information. /// public DataProtectionConfiguration DisableAutomaticKeyGeneration() { Services.Configure(options => { options.AutoGenerateKeys = false; }); return this; } /// /// Configures the data protection system to persist keys to the specified directory. /// This path may be on the local machine or may point to a UNC share. /// /// The directory in which to store keys. /// The 'this' instance. public DataProtectionConfiguration PersistKeysToFileSystem(DirectoryInfo directory) { if (directory == null) { throw new ArgumentNullException(nameof(directory)); } Use(DataProtectionServiceDescriptors.IXmlRepository_FileSystem(directory)); return this; } /// /// Configures the data protection system to persist keys to the Windows registry. /// /// The location in the registry where keys should be stored. /// The 'this' instance. public DataProtectionConfiguration PersistKeysToRegistry(RegistryKey registryKey) { if (registryKey == null) { throw new ArgumentNullException(nameof(registryKey)); } Use(DataProtectionServiceDescriptors.IXmlRepository_Registry(registryKey)); return this; } #if !DOTNET5_4 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml /// /// Configures keys to be encrypted to a given certificate before being persisted to storage. /// /// The certificate to use when encrypting keys. /// The 'this' instance. public DataProtectionConfiguration ProtectKeysWithCertificate(X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } Use(DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(certificate)); return this; } /// /// Configures keys to be encrypted to a given certificate before being persisted to storage. /// /// The thumbprint of the certificate to use when encrypting keys. /// The 'this' instance. public DataProtectionConfiguration ProtectKeysWithCertificate(string thumbprint) { if (thumbprint == null) { throw new ArgumentNullException(nameof(thumbprint)); } // Make sure the thumbprint corresponds to a valid certificate. if (new CertificateResolver().ResolveCertificate(thumbprint) == null) { throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint); } // ICertificateResolver is necessary for this type to work correctly, so register it // if it doesn't already exist. Services.TryAdd(DataProtectionServiceDescriptors.ICertificateResolver_Default()); Use(DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(thumbprint)); return this; } #endif /// /// Configures keys to be encrypted with Windows DPAPI before being persisted to /// storage. The encrypted key will only be decryptable by the current Windows user account. /// /// The 'this' instance. /// /// This API is only supported on Windows platforms. /// public DataProtectionConfiguration ProtectKeysWithDpapi() { return ProtectKeysWithDpapi(protectToLocalMachine: false); } /// /// Configures keys to be encrypted with Windows DPAPI before being persisted to /// storage. /// /// 'true' if the key should be decryptable by any /// use on the local machine, 'false' if the key should only be decryptable by the current /// Windows user account. /// The 'this' instance. /// /// This API is only supported on Windows platforms. /// public DataProtectionConfiguration ProtectKeysWithDpapi(bool protectToLocalMachine) { Use(DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToLocalMachine)); return this; } /// /// Configures keys to be encrypted with Windows CNG DPAPI before being persisted /// to storage. The keys will be decryptable by the current Windows user account. /// /// The 'this' instance. /// /// See https://msdn.microsoft.com/en-us/library/windows/desktop/hh706794(v=vs.85).aspx /// for more information on DPAPI-NG. This API is only supported on Windows 8 / Windows Server 2012 and higher. /// public DataProtectionConfiguration ProtectKeysWithDpapiNG() { return ProtectKeysWithDpapiNG( protectionDescriptorRule: DpapiNGXmlEncryptor.GetDefaultProtectionDescriptorString(), flags: DpapiNGProtectionDescriptorFlags.None); } /// /// Configures keys to be encrypted with Windows CNG DPAPI before being persisted to storage. /// /// The descriptor rule string with which to protect the key material. /// Flags that should be passed to the call to 'NCryptCreateProtectionDescriptor'. /// The default value of this parameter is . /// The 'this' instance. /// /// See https://msdn.microsoft.com/en-us/library/windows/desktop/hh769091(v=vs.85).aspx /// and https://msdn.microsoft.com/en-us/library/windows/desktop/hh706800(v=vs.85).aspx /// for more information on valid values for the the /// and arguments. /// This API is only supported on Windows 8 / Windows Server 2012 and higher. /// public DataProtectionConfiguration ProtectKeysWithDpapiNG(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags) { if (protectionDescriptorRule == null) { throw new ArgumentNullException(nameof(protectionDescriptorRule)); } Use(DataProtectionServiceDescriptors.IXmlEncryptor_DpapiNG(protectionDescriptorRule, flags)); return this; } /// /// Sets the unique name of this application within the data protection system. /// /// The application name. /// The 'this' instance. /// /// This API corresponds to setting the property /// to the value of . /// public DataProtectionConfiguration SetApplicationName(string applicationName) { return ConfigureGlobalOptions(options => { options.ApplicationDiscriminator = applicationName; }); } /// /// Sets the default lifetime of keys created by the data protection system. /// /// The lifetime (time before expiration) for newly-created keys. /// See for more information and /// usage notes. /// The 'this' instance. public DataProtectionConfiguration SetDefaultKeyLifetime(TimeSpan lifetime) { Services.Configure(options => { options.NewKeyLifetime = lifetime; }); return this; } /// /// Configures the data protection system to use the specified cryptographic algorithms /// by default when generating protected payloads. /// /// Information about what cryptographic algorithms should be used. /// The 'this' instance. public DataProtectionConfiguration UseCryptographicAlgorithms(AuthenticatedEncryptionOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } return UseCryptographicAlgorithmsCore(options); } /// /// Configures the data protection system to use custom Windows CNG algorithms. /// This API is intended for advanced scenarios where the developer cannot use the /// algorithms specified in the and /// enumerations. /// /// Information about what cryptographic algorithms should be used. /// The 'this' instance. /// /// This API is only available on Windows. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public DataProtectionConfiguration UseCustomCryptographicAlgorithms(CngCbcAuthenticatedEncryptionOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } return UseCryptographicAlgorithmsCore(options); } /// /// Configures the data protection system to use custom Windows CNG algorithms. /// This API is intended for advanced scenarios where the developer cannot use the /// algorithms specified in the and /// enumerations. /// /// Information about what cryptographic algorithms should be used. /// The 'this' instance. /// /// This API is only available on Windows. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public DataProtectionConfiguration UseCustomCryptographicAlgorithms(CngGcmAuthenticatedEncryptionOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } return UseCryptographicAlgorithmsCore(options); } /// /// Configures the data protection system to use custom algorithms. /// This API is intended for advanced scenarios where the developer cannot use the /// algorithms specified in the and /// enumerations. /// /// Information about what cryptographic algorithms should be used. /// The 'this' instance. [EditorBrowsable(EditorBrowsableState.Advanced)] public DataProtectionConfiguration UseCustomCryptographicAlgorithms(ManagedAuthenticatedEncryptionOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } return UseCryptographicAlgorithmsCore(options); } private DataProtectionConfiguration UseCryptographicAlgorithmsCore(IInternalAuthenticatedEncryptionOptions options) { options.Validate(); // perform self-test Use(DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromOptions(options)); return this; } /// /// Configures the data protection system to use the /// for data protection services. /// /// The 'this' instance. /// /// If this option is used, payloads protected by the data protection system will /// be permanently undecipherable after the application exits. /// public DataProtectionConfiguration UseEphemeralDataProtectionProvider() { Use(DataProtectionServiceDescriptors.IDataProtectionProvider_Ephemeral()); return this; } /* * UTILITY ISERVICECOLLECTION METHODS */ private void RemoveAllServicesOfType(Type serviceType) { // We go backward since we're modifying the collection in-place. for (int i = Services.Count - 1; i >= 0; i--) { if (Services[i]?.ServiceType == serviceType) { Services.RemoveAt(i); } } } private void Use(ServiceDescriptor descriptor) { RemoveAllServicesOfType(descriptor.ServiceType); Services.Add(descriptor); } } }