//-----------------------------------------------------------------------
//
// 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);
}
}