//----------------------------------------------------------------------- // // Copyright (c) aliasvault. All rights reserved. // Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information. // //----------------------------------------------------------------------- namespace AliasVault.Cryptography.Server; using System.Security.Cryptography.X509Certificates; using AliasServerDb; using AliasVault.Shared.Server.Utilities; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; /// /// Helper utility to configure DataProtection for web projects. /// public static class DataProtectionExtensions { /// /// Setup .NET DataProtection to use common AliasVault settings. /// /// Services. /// Application name. /// IServiceCollection. /// Thrown if environment variable is not set. public static IServiceCollection AddAliasVaultDataProtection( this IServiceCollection services, string applicationName) { var dataProtectionBuilder = services.AddDataProtection() .PersistKeysToDbContext() .SetApplicationName(applicationName); if (SecretReader.IsRunningInContainer()) { ConfigureContainerDataProtection(dataProtectionBuilder); } else { ConfigureDevelopmentDataProtection(dataProtectionBuilder, applicationName); } return services; } /// /// Configure data protection for container environments. /// /// The data protection builder. private static void ConfigureContainerDataProtection(IDataProtectionBuilder dataProtectionBuilder) { // When running in containers, don't use certificate-based key protection due to Linux keystore limitations // Keys are protected by database access controls, TLS, and container isolation dataProtectionBuilder .UseCryptographicAlgorithms(new Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorConfiguration() { EncryptionAlgorithm = Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.EncryptionAlgorithm.AES_256_CBC, ValidationAlgorithm = Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ValidationAlgorithm.HMACSHA256, }) .SetDefaultKeyLifetime(TimeSpan.FromDays(90)); } /// /// Configure data protection for development environments. /// /// The data protection builder. /// The application name. private static void ConfigureDevelopmentDataProtection(IDataProtectionBuilder dataProtectionBuilder, string applicationName) { // Not in container, get certificate password using SecretReader var certPassword = SecretReader.GetDataProtectionCertPassword(); var certPath = $"../../certificates/app/{applicationName}.DataProtection.pfx"; if (certPassword == "Development") { certPath = Path.Combine(AppContext.BaseDirectory, $"{applicationName}.DataProtection.Development.pfx"); } // Use certificate-based protection for development var certificateFlags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; X509Certificate2 cert; if (!File.Exists(certPath)) { cert = CertificateGenerator.GeneratePfx($"{applicationName}.DataProtection", certPassword); CertificateGenerator.SaveCertificateToFile(cert, certPassword, certPath); } else { cert = X509CertificateLoader.LoadPkcs12FromFile(certPath, certPassword, certificateFlags); } dataProtectionBuilder.ProtectKeysWithCertificate(cert); } }