/// <summary>
    /// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
    /// </summary>
    /// <typeparam name="TImplementation">The concrete type of the <see cref="IKeyEscrowSink"/> to register.</typeparam>
    /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
    /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
    /// <remarks>
    /// Registrations are additive. The factory is registered as <see cref="ServiceLifetime.Singleton"/>.
    /// </remarks>
    public static IDataProtectionBuilder AddKeyEscrowSink <TImplementation>(this IDataProtectionBuilder builder)
        where TImplementation : class, IKeyEscrowSink
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
        {
            var implementationInstance = services.GetRequiredService <TImplementation>();
            return(new ConfigureOptions <KeyManagementOptions>(options =>
            {
                options.KeyEscrowSinks.Add(implementationInstance);
            }));
        });

        return(builder);
    }
    /// <summary>
    /// Sets the default lifetime of keys created by the data protection system.
    /// </summary>
    /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
    /// <param name="lifetime">The lifetime (time before expiration) for newly-created keys.
    /// See <see cref="KeyManagementOptions.NewKeyLifetime"/> for more information and
    /// usage notes.</param>
    /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
    public static IDataProtectionBuilder SetDefaultKeyLifetime(this IDataProtectionBuilder builder, TimeSpan lifetime)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (lifetime < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(Resources.FormatLifetimeMustNotBeNegative(nameof(lifetime)));
        }

        builder.Services.Configure <KeyManagementOptions>(options =>
        {
            options.NewKeyLifetime = lifetime;
        });

        return(builder);
    }
예제 #3
0
        /// <summary>
        /// Configures keys to be encrypted with Windows DPAPI before being persisted to
        /// storage.
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
        /// <param name="protectToLocalMachine">'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.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
        /// <remarks>
        /// This API is only supported on Windows platforms.
        /// </remarks>
        public static IDataProtectionBuilder ProtectKeysWithDpapi(this IDataProtectionBuilder builder, bool protectToLocalMachine)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
            {
                var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    CryptoUtil.AssertPlatformIsWindows();
                    options.XmlEncryptor = new DpapiXmlEncryptor(protectToLocalMachine, loggerFactory);
                }));
            });

            return(builder);
        }
    /// <summary>
    /// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
    /// </summary>
    /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
    /// <param name="sink">The instance of the <see cref="IKeyEscrowSink"/> to register.</param>
    /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
    /// <remarks>
    /// Registrations are additive.
    /// </remarks>
    public static IDataProtectionBuilder AddKeyEscrowSink(this IDataProtectionBuilder builder, IKeyEscrowSink sink)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (sink == null)
        {
            throw new ArgumentNullException(nameof(sink));
        }

        builder.Services.Configure <KeyManagementOptions>(options =>
        {
            options.KeyEscrowSinks.Add(sink);
        });

        return(builder);
    }
        public static IServiceCollection AddFullDataProtection(this IServiceCollection services, string?appDiscriminator, string?blobConnectionString, string?keyName, Uri?keyIdentifier, string?tenantId, string?clientId, string?clientSecret)
        {
            if (string.IsNullOrWhiteSpace(appDiscriminator))
            {
                throw new System.ArgumentException("A value must be provided.", nameof(appDiscriminator));
            }

            if (string.IsNullOrWhiteSpace(blobConnectionString))
            {
                throw new System.ArgumentException("A value must be provided.", nameof(blobConnectionString));
            }

            if (string.IsNullOrWhiteSpace(keyName))
            {
                throw new System.ArgumentException("A value must be provided.", nameof(keyName));
            }

            if (keyIdentifier == null)
            {
                throw new System.ArgumentException("A value must be provided.", nameof(keyIdentifier));
            }

            if (string.IsNullOrWhiteSpace(clientId))
            {
                throw new System.ArgumentException("A value must be provided.", nameof(clientId));
            }

            if (string.IsNullOrWhiteSpace(clientSecret))
            {
                throw new System.ArgumentException("A value must be provided.", nameof(clientSecret));
            }

            // See: https://docs.microsoft.com/en-us/rest/api/storageservices/Naming-and-Referencing-Containers--Blobs--and-Metadata#container-names
            var containerName = $"dataprotection-{appDiscriminator}";

            IDataProtectionBuilder dataProtectionBuilder = services
                                                           .AddDataProtection(o => o.ApplicationDiscriminator = appDiscriminator)
                                                           .PersistKeysToAzureBlobStorage(blobConnectionString, containerName, keyName)
                                                           .ProtectKeysWithAzureKeyVault(keyIdentifier, new Azure.Identity.ClientSecretCredential(tenantId, clientId, clientSecret));

            return(services);
        }
예제 #6
0
    /// <summary>
    /// Configures certificates which can be used to decrypt keys loaded from storage.
    /// </summary>
    /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
    /// <param name="certificates">Certificates that can be used to decrypt key data.</param>
    /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
    public static IDataProtectionBuilder UnprotectKeysWithAnyCertificate(this IDataProtectionBuilder builder, params X509Certificate2[] certificates)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        builder.Services.Configure <XmlKeyDecryptionOptions>(o =>
        {
            if (certificates != null)
            {
                foreach (var certificate in certificates)
                {
                    o.AddKeyDecryptionCertificate(certificate);
                }
            }
        });

        return(builder);
    }
예제 #7
0
        /// <summary>
        /// Configures the data protection system to persist keys to file storage.
        /// </summary>
        /// <param name="builder">The builder instance to modify.</param>
        /// <param name="storageFactory">The storage factory to use.</param>
        /// <returns>The value <paramref name="builder"/>.</returns>
        public static IDataProtectionBuilder PersistKeysToFileStorage(this IDataProtectionBuilder builder, Func <IServiceProvider, IFileStorage> storageFactory)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services => {
                var storage = storageFactory?.Invoke(services);
                if (storage == null)
                {
                    throw new ArgumentNullException(nameof(storageFactory));
                }

                var loggerFactory = services.GetService <ILoggerFactory>();
                return(new ConfigureOptions <KeyManagementOptions>(options => options.XmlRepository = new FoundatioStorageXmlRepository(storage, loggerFactory)));
            });

            return(builder);
        }
예제 #8
0
        /// <summary>
        /// Configures the data protection system to persist keys to the specified database and collection in MongoDB.
        /// </summary>
        /// <param name="builder">The builder instance to modify.</param>
        /// <param name="collection">Collection used to store the key list.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder"/> after this operation has completed.</returns>
        public static IDataProtectionBuilder PersistKeysToMongoDb(this IDataProtectionBuilder builder, IMongoCollection <MongoDbXmlKey> collection)
        {
            if (builder is null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (collection is null)
            {
                throw new ArgumentNullException(nameof(collection));
            }

            builder.Services.Configure <KeyManagementOptions>(options =>
            {
                var mongoDbXmlRepository = new MongoDbXmlRepository(collection);
                options.XmlRepository    = mongoDbXmlRepository;
            });

            return(builder);
        }
예제 #9
0
        /// <summary>
        /// Configures the data protection system to encrypt keys using AWS Key Management Service master keys
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
        /// <param name="config">The configuration object specifying how use KMS keys.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
        public static IDataProtectionBuilder ProtectKeysWithAwsKms(this IDataProtectionBuilder builder, KmsXmlEncryptorConfig config)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            Use(builder.Services,
                ServiceDescriptor.Singleton <IXmlEncryptor>(services => new KmsXmlEncryptor(services.GetRequiredService <IAmazonKeyManagementService>(), config, services)));
            // Need to ensure KmsXmlDecryptor can actually be constructed
            Use(builder.Services, ServiceDescriptor.Singleton(config));
            Use(builder.Services, ServiceDescriptor.Singleton <IKmsXmlEncryptorConfig>(config));
            Use(builder.Services, ServiceDescriptor.Singleton(services => new KmsXmlDecryptor(services)));
            Use(builder.Services, ServiceDescriptor.Singleton <IXmlDecryptor>(services => services.GetRequiredService <KmsXmlDecryptor>()));
            return(builder);
        }
예제 #10
0
        public static void AddWebAppOptions(this IServiceCollection ext, WebApplicationOptions options = null)
        {
            ext     = ext ?? throw new ArgumentNullException(nameof(ext));
            options = options ?? new WebApplicationOptions();

            ext.ConfigureApplicationCookie(opt => {
                opt.Cookie.Name = options.ApplicationCookieName ?? (options.CookieBaseName != null ? $"{options.CookieBaseName}_app" : opt.Cookie.Name);
            });
            ext.ConfigureExternalCookie(opt => {
                opt.Cookie.Name = options.ExternalCookieName ?? (options.CookieBaseName != null ? $"{options.CookieBaseName}_ext" : opt.Cookie.Name);
            });

            IDataProtectionBuilder dpBuilder = ext.AddDataProtection()
                                               .SetApplicationName(options.DataProtection.ApplicationName)
                                               .PersistKeysToFileSystem(new DirectoryInfo(options.DataProtection.KeyRingPath));

            if (options.DataProtection.DisableAutomaticKeyGeneration)
            {
                dpBuilder.DisableAutomaticKeyGeneration();
            }
        }
        protected internal override void AddInternal(IDataProtectionBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            var path = this.Path;

            if (string.IsNullOrWhiteSpace(path))
            {
                throw new InvalidOperationException("The path is not set.");
            }

            if (!System.IO.Path.IsPathRooted(path))
            {
                path = System.IO.Path.Combine(builder.HostEnvironment.ContentRootPath, path);
            }

            builder.PersistKeysToFileSystem(new DirectoryInfo(path));
        }
        /// <summary>
        /// Configures the data protection system to persist keys to the specified path
        /// in Azure Blob Storage.
        /// </summary>
        /// <param name="builder">The builder instance to modify.</param>
        /// <param name="blobReference">The <see cref="CloudBlockBlob"/> where the
        /// key file should be stored.</param>
        /// <returns>The value <paramref name="builder"/>.</returns>
        /// <remarks>
        /// The container referenced by <paramref name="blobReference"/> must already exist.
        /// </remarks>
        public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudBlockBlob blobReference)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            if (blobReference == null)
            {
                throw new ArgumentNullException(nameof(blobReference));
            }

            // We're basically just going to make a copy of this blob.
            // Use (container, blobName) instead of (storageuri, creds) since the container
            // is tied to an existing service client, which contains user-settable defaults
            // like retry policy and secondary connection URIs.

            var container = blobReference.Container;
            var blobName  = blobReference.Name;

            return(PersistKeystoAzureBlobStorageInternal(builder, () => container.GetBlockBlobReference(blobName)));
        }
 public static IDataProtectionBuilder PersistKeysToMongoDb(this IDataProtectionBuilder builder,
                                                           Func <IServiceProvider, IMongoDatabase> databaseFactory,
                                                           string collectionName = DataProtectionKeysCollectionName)
 {
     if (builder == null)
     {
         throw new ArgumentNullException(nameof(builder));
     }
     if (databaseFactory == null)
     {
         throw new ArgumentNullException(nameof(databaseFactory));
     }
     builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
     {
         return(new ConfigureOptions <KeyManagementOptions>(options =>
         {
             options.XmlRepository = new MongoDbXmlRepository(() => databaseFactory(services), collectionName);
         }));
     });
     return(builder);
 }
        protected internal override void AddInternal(IDataProtectionBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            builder.Services.AddDbContext <DataProtectionContext, SqlServerDataProtectionContext>(optionsBuilder =>
                                                                                                  optionsBuilder.UseSqlServer(
                                                                                                      this.GetConnectionString(builder.Configuration),
                                                                                                      options =>
            {
                if (this.MigrationsAssembly != null)
                {
                    options.MigrationsAssembly(this.MigrationsAssembly);
                }
            }
                                                                                                      ));

            base.AddInternal(builder);
        }
        /// <summary>
        /// Configures the data protection system to persist keys to a specified S3 bucket.
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
        /// <param name="s3Client">S3 client configured with appropriate credentials.</param>
        /// <param name="config">The configuration object specifying how to write to S3.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
        public static IDataProtectionBuilder PersistKeysToAwsS3(this IDataProtectionBuilder builder, IAmazonS3 s3Client, S3XmlRepositoryConfig config)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (s3Client == null)
            {
                throw new ArgumentNullException(nameof(s3Client));
            }

            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            Use(builder.Services, ServiceDescriptor.Singleton <IMockingWrapper, MockingWrapper>());
            Use(builder.Services, ServiceDescriptor.Singleton <IXmlRepository>(services => new S3XmlRepository(s3Client, config, services)));
            return(builder);
        }
        /// <summary>
        /// Configures the data protection system to persist the key-ring as a secret in Azure Key Vault
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
        /// <param name="keyRingName">The name of the secret that will hold the key-ring</param>
        /// <param name="vaultUrl">The base URL to your Azure Key Vault</param>
        /// <param name="clientId">The azure clientId</param>
        /// <param name="tenantId">The azure tenantId</param>
        /// <param name="secret">The azure client secret</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
        public static IDataProtectionBuilder PersistKeysToAzureKeyVault(this IDataProtectionBuilder builder,
                                                                        string keyRingName,
                                                                        string vaultUrl,
                                                                        string clientId,
                                                                        string tenantId,
                                                                        string secret)
        {
            if (string.IsNullOrEmpty(keyRingName))
            {
                throw new ArgumentNullException(nameof(keyRingName));
            }
            if (string.IsNullOrEmpty(vaultUrl))
            {
                throw new ArgumentNullException(nameof(vaultUrl));
            }
            if (string.IsNullOrEmpty(clientId))
            {
                throw new ArgumentNullException(nameof(clientId));
            }
            if (string.IsNullOrEmpty(tenantId))
            {
                throw new ArgumentNullException(nameof(tenantId));
            }
            if (string.IsNullOrEmpty(secret))
            {
                throw new ArgumentNullException(nameof(secret));
            }

            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
            {
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;

                    options.XmlRepository = new AzureKeyVaultKeyRingRepository(keyRingName, vaultUrl, clientId, tenantId, secret, loggerFactory);
                }));
            });

            return(builder);
        }
        /// <summary>
        /// Sets up data protection to persist session keys in Redis.
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/> used to set up data protection options.</param>
        /// <param name="redisConnectionString">The connection string specifying the Redis instance and database for key storage.</param>
        /// <returns>
        /// The <paramref name="builder" /> for continued configuration.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="builder" /> or <paramref name="redisConnectionString" /> is <see langword="null" />.
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// Thrown if <paramref name="redisConnectionString" /> is empty.
        /// </exception>
        public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (redisConnectionString == null)
            {
                throw new ArgumentNullException(nameof(redisConnectionString));
            }

            if (redisConnectionString.Length == 0)
            {
                throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString));
            }

            var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result;

            return(builder.Use(ServiceDescriptor.Singleton <IXmlRepository>(services =>
                                                                            new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService <ILogger <RedisXmlRepository> >()))));
        }
        /// <summary>
        /// Add Keyvault protection
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="configuration"></param>
        public static IDataProtectionBuilder AddAzureKeyVaultDataProtection(
            this IDataProtectionBuilder builder, IConfiguration configuration)
        {
            var config = new DataProtectionConfig(configuration);

            if (string.IsNullOrEmpty(config.KeyVaultBaseUrl))
            {
                throw new InvalidConfigurationException(
                          "Keyvault base url is missing in your configuration " +
                          "for dataprotection to be able to store the root key.");
            }
            var keyName  = config.KeyVaultKeyDataProtection;
            var keyVault = new KeyVaultClientBootstrap(configuration);

            if (!TryInititalizeKeyAsync(keyVault.Client, config.KeyVaultBaseUrl, keyName).Result)
            {
                throw new UnauthorizedAccessException("Cannot access keyvault");
            }
            var identifier = $"{config.KeyVaultBaseUrl.TrimEnd('/')}/keys/{keyName}";

            return(builder.ProtectKeysWithAzureKeyVault(keyVault.Client, identifier));
        }
        public static IDataProtectionBuilder PersistKeysToAwsS3(this IDataProtectionBuilder builder, IAmazonS3 client,
                                                                S3XmlRepositoryConfig config)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            builder.Services.Configure <KeyManagementOptions>(options =>
            {
                options.XmlRepository = new S3XmlRepository(client, config);
            });
            return(builder);
        }
예제 #20
0
        public static IDataProtectionBuilder PersistKeys(this IDataProtectionBuilder builder, Func <CacheOptions> configure = null)
        {
            var options = (configure != null)
                ? configure()
                : new CacheOptions();

            if (System.String.IsNullOrWhiteSpace(options?.RedisUrl))
            {
                builder.PersistKeysToFileSystem(
                    new DirectoryInfo(Path.Combine(options.SharedFolder, options.DataProtectionFolder))
                    );
            }
            else
            {
                builder.PersistKeysToStackExchangeRedis(
                    ConnectionMultiplexer.Connect(options.RedisUrl),
                    $"{options.Key}-dpk"
                    );
            }

            return(builder);
        }
예제 #21
0
    public static IDataProtectionBuilder PersistKeysToRegistry(this IDataProtectionBuilder builder, RegistryKey registryKey)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (registryKey == null)
        {
            throw new ArgumentNullException(nameof(registryKey));
        }

        builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
        {
            var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
            return(new ConfigureOptions <KeyManagementOptions>(options =>
            {
                options.XmlRepository = new RegistryXmlRepository(registryKey, loggerFactory);
            }));
        });

        return(builder);
    }
        /// <summary>
        /// Add blob key storage
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="configuration"></param>
        public static IDataProtectionBuilder AddAzureBlobKeyStorage(
            this IDataProtectionBuilder builder, IConfiguration configuration)
        {
            var storage          = new DataProtectionConfig(configuration);
            var containerName    = storage.BlobStorageContainerDataProtection;
            var connectionString = storage.GetStorageConnString();

            if (string.IsNullOrEmpty(connectionString))
            {
                throw new InvalidConfigurationException(
                          "Storage configuration is missing in your configuration for " +
                          "dataprotection to store all keys across all instances.");
            }
            var storageAccount = CloudStorageAccount.Parse(storage.GetStorageConnString());
            var relativePath   = $"{containerName}/keys.xml";
            var uriBuilder     = new UriBuilder(storageAccount.BlobEndpoint);

            uriBuilder.Path = uriBuilder.Path.TrimEnd('/') + "/" + relativePath.TrimStart('/');
            var block = new CloudBlockBlob(uriBuilder.Uri, storageAccount.Credentials);

            Try.Op(() => block.Container.Create());
            return(builder.PersistKeysToAzureBlobStorage(block));
        }
예제 #23
0
        /// <summary>
        /// Configures the data protection system to persist keys to an EntityFrameworkCore store
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/> instance to modify.</param>
        /// <returns>The value <paramref name="builder"/>.</returns>
        public static IDataProtectionBuilder PersistKeysToDbContext <TContext>(this IDataProtectionBuilder builder)
            where TContext : DbContext
        {
            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(provider =>
            {
                var loggerFactory = provider.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    options.XmlRepository = new XmlRepository <TContext>(provider, loggerFactory);
                }));
            });
            builder.Services.AddSingleton <IXmlRepository, XmlRepository <TContext> >();
            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(serviceProvider =>
            {
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    serviceProvider.RunScopedService <IXmlRepository>(xmlRepository =>
                                                                      options.XmlRepository = xmlRepository);
                }));
            });

            return(builder);
        }
예제 #24
0
        /// <summary>
        /// Removes keys from the MongoDB repository after they expire or are revoked.
        /// </summary>
        /// <param name="builder">The builder instance to modify.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder"/> after this operation has completed.</returns>
        /// <exception cref="InvalidOperationException">PersistKeysToMongoDb must be called before this method.</exception>
        /// <remarks>
        /// Cleanup will run after this method is called, and whenever the key manager calls <see cref="Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository.StoreElement"/>.
        /// </remarks>
        /// <remarks>
        /// If a custom key manager is used, it must be configured before calling this method and keys must have an "id" attribute on the top level element.
        /// </remarks>
        public static IDataProtectionBuilder AddKeyCleanup(this IDataProtectionBuilder builder)
        {
            if (builder is null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            var keyManager = builder.Services.BuildServiceProvider().GetService <IKeyManager>();

            builder.Services.Configure <KeyManagementOptions>(options =>
            {
                if (options.XmlRepository is MongoDbXmlRepository mongodbXmlRepository)
                {
                    mongodbXmlRepository.SetKeyManager(keyManager);
                }
                else
                {
                    throw new InvalidOperationException($"{nameof(PersistKeysToMongoDb)} must be called before {nameof(AddKeyCleanup)}.");
                }
            });

            return(builder);
        }
예제 #25
0
    /// <summary>
    /// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
    /// </summary>
    /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
    /// <param name="factory">A factory that creates the <see cref="IKeyEscrowSink"/> instance.</param>
    /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
    /// <remarks>
    /// Registrations are additive. The factory is registered as <see cref="ServiceLifetime.Singleton"/>.
    /// </remarks>
    public static IDataProtectionBuilder AddKeyEscrowSink(this IDataProtectionBuilder builder, Func <IServiceProvider, IKeyEscrowSink> factory)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (factory == null)
        {
            throw new ArgumentNullException(nameof(factory));
        }

        builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
        {
            var instance = factory(services);
            return(new ConfigureOptions <KeyManagementOptions>(options =>
            {
                options.KeyEscrowSinks.Add(instance);
            }));
        });

        return(builder);
    }
예제 #26
0
        /// <summary>
        /// Configures keys to be encrypted to a given certificate before being persisted to storage.
        /// </summary>
        /// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
        /// <param name="certificate">The certificate to use when encrypting keys.</param>
        /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
        public static IDataProtectionBuilder ProtectKeysWithCertificate(this IDataProtectionBuilder builder, X509Certificate2 certificate)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (certificate == null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }

            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
            {
                var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    options.XmlEncryptor = new CertificateXmlEncryptor(certificate, loggerFactory);
                }));
            });

            return(builder);
        }
예제 #27
0
        /// <summary>
        /// Configures the data protection system to persist keys to the specified path
        /// in Azure Blob Storage.
        /// </summary>
        /// <param name="builder">The builder instance to modify.</param>
        /// <param name="connectionString">A connection string includes the authentication information
        /// required for your application to access data in an Azure Storage
        /// account at runtime.
        /// </param>
        /// <param name="containerName">The container name to use.</param>
        /// <param name="blobName">The blob name to use.</param>
        /// <returns>The value <paramref name="builder"/>.</returns>
        /// <remarks>
        /// The container referenced by <paramref name="containerName"/><paramref name="blobName"/> must already exist.
        /// </remarks>
        public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, string connectionString, string containerName, string blobName)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            if (connectionString == null)
            {
                throw new ArgumentNullException(nameof(connectionString));
            }
            if (containerName == null)
            {
                throw new ArgumentNullException(nameof(containerName));
            }
            if (blobName == null)
            {
                throw new ArgumentNullException(nameof(blobName));
            }

            var client = new BlobServiceClient(connectionString).GetBlobContainerClient(containerName).GetBlobClient(blobName);

            return(PersistKeysToAzureBlobStorage(builder, client));
        }
예제 #28
0
        public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (descriptor == null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            for (int i = builder.Services.Count - 1; i >= 0; i--)
            {
                if (builder.Services[i]?.ServiceType == descriptor.ServiceType)
                {
                    builder.Services.RemoveAt(i);
                }
            }

            builder.Services.Add(descriptor);

            return(builder);
        }
예제 #29
0
        public static IDataProtectionBuilder PersistKeysToMongoDb(this IDataProtectionBuilder builder, Func <IServiceProvider, IMongoCollection <mongoDb.DataProtectionKey> > getCollection = null)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (getCollection == null)
            {
                getCollection = p => p.GetRequiredService <IMongoDatabase>().GetCollection <mongoDb.DataProtectionKey>(nameof(mongoDb.DataProtectionKey));
            }

            builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
            {
                var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
                return(new ConfigureOptions <KeyManagementOptions>(options =>
                {
                    options.XmlRepository = new mongoDb.MongoDbXmlRepository <mongoDb.DataProtectionKey>(services, loggerFactory);
                }));
            })
            .AddTransient(p => new mongoDb.MongoCollectionWrapper <mongoDb.DataProtectionKey>(getCollection(p)));

            return(builder);
        }
예제 #30
0
    public static IDataProtectionBuilder ProtectKeysWithDpapiNG(this IDataProtectionBuilder builder, string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (protectionDescriptorRule == null)
        {
            throw new ArgumentNullException(nameof(protectionDescriptorRule));
        }

        builder.Services.AddSingleton <IConfigureOptions <KeyManagementOptions> >(services =>
        {
            var loggerFactory = services.GetService <ILoggerFactory>() ?? NullLoggerFactory.Instance;
            return(new ConfigureOptions <KeyManagementOptions>(options =>
            {
                CryptoUtil.AssertPlatformIsWindows8OrLater();
                options.XmlEncryptor = new DpapiNGXmlEncryptor(protectionDescriptorRule, flags, loggerFactory);
            }));
        });

        return(builder);
    }