refactor(CryptoFactoryParams): PemFileInitalizer erstellt, um das Lesen und Aktualisieren von Pem-Dateien zu ermöglichen.
- Minimierung der di-Erweiterungsmethoden. - AfterCreate-Methode entfernt
This commit is contained in:
parent
528a346883
commit
144fe86987
@ -44,61 +44,5 @@ namespace DigitalData.Core.Security.Config
|
|||||||
public IEnumerable<RSATokenDescriptor> TokenDescriptors { get; init; } = new List<RSATokenDescriptor>();
|
public IEnumerable<RSATokenDescriptor> TokenDescriptors { get; init; } = new List<RSATokenDescriptor>();
|
||||||
|
|
||||||
public RSADecryptor? VaultDecryptor { get; init; }
|
public RSADecryptor? VaultDecryptor { get; init; }
|
||||||
|
|
||||||
public CryptoFactoryParams()
|
|
||||||
{
|
|
||||||
// init decryptors
|
|
||||||
AfterCreate += () =>
|
|
||||||
{
|
|
||||||
// Create root folder if it does not exist
|
|
||||||
if (!Directory.Exists(PemDirectory))
|
|
||||||
Directory.CreateDirectory(PemDirectory);
|
|
||||||
|
|
||||||
var privateKeys = new List<RSAPrivateKey>();
|
|
||||||
privateKeys.AddRange(Decryptors);
|
|
||||||
privateKeys.AddRange(TokenDescriptors);
|
|
||||||
if (VaultDecryptor is not null)
|
|
||||||
privateKeys.Add(VaultDecryptor);
|
|
||||||
|
|
||||||
foreach (var privateKey in privateKeys)
|
|
||||||
{
|
|
||||||
// set default path
|
|
||||||
if (privateKey.IsPemNull)
|
|
||||||
{
|
|
||||||
// file name
|
|
||||||
var file_name_params = new List<object>();
|
|
||||||
|
|
||||||
if (privateKey.Id is not null)
|
|
||||||
file_name_params.Add(privateKey.Id);
|
|
||||||
else if (privateKey is RSATokenDescriptor descriptor)
|
|
||||||
file_name_params.Add(descriptor.Issuer);
|
|
||||||
|
|
||||||
file_name_params.Add(KeySizeInBits);
|
|
||||||
file_name_params.Add(DateTime.Now.ToTag(DateTagFormat));
|
|
||||||
|
|
||||||
if (privateKey.IsEncrypted)
|
|
||||||
file_name_params.Add(Secrets.Version);
|
|
||||||
|
|
||||||
var file_name = $"{string.Join(FileNameSeparator, file_name_params)}.{FileExtension}";
|
|
||||||
|
|
||||||
var path = Path.Combine(PemDirectory, file_name);
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
privateKey.SetPem(File.ReadAllText(path));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var pem = privateKey.IsEncrypted
|
|
||||||
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(pbeParameters: PbeParameters, keySizeInBits: KeySizeInBits, password: Secrets.PBE_PASSWORD)
|
|
||||||
: Instance.RSAFactory.CreatePrivateKeyPem(keySizeInBits: KeySizeInBits);
|
|
||||||
|
|
||||||
privateKey.SetPem(pem);
|
|
||||||
|
|
||||||
// Save file in background
|
|
||||||
Task.Run(async () => await File.WriteAllTextAsync(path: path, pem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Config
|
|
||||||
{
|
|
||||||
public class ParamsConfigureOptions<TParams> : IConfigureOptions<TParams> where TParams : RSAFactoryParams
|
|
||||||
{
|
|
||||||
public void Configure(TParams options) => options.Init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace DigitalData.Core.Security.Config
|
namespace DigitalData.Core.Security.Config
|
||||||
{
|
{
|
||||||
public class RSAFactoryParams : IJsonOnDeserialized
|
public class RSAFactoryParams
|
||||||
{
|
{
|
||||||
public int KeySizeInBits { get; init; } = 2048;
|
public int KeySizeInBits { get; init; } = 2048;
|
||||||
|
|
||||||
@ -27,33 +27,14 @@ namespace DigitalData.Core.Security.Config
|
|||||||
|
|
||||||
public string EncryptedPrivateKeyPemLabel { get; init; } = "ENCRYPTED PRIVATE KEY";
|
public string EncryptedPrivateKeyPemLabel { get; init; } = "ENCRYPTED PRIVATE KEY";
|
||||||
|
|
||||||
private PbeParameters? _pbeParameters;
|
private readonly Lazy<PbeParameters> _lazyPbeParameters;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public PbeParameters PbeParameters => _pbeParameters!;
|
public PbeParameters PbeParameters => _lazyPbeParameters.Value;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a thread-safe initialization mechanism using Lazy initialization.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Lazy<bool> _lazyInitializer;
|
|
||||||
|
|
||||||
public bool IsInitialized => _lazyInitializer.IsValueCreated;
|
|
||||||
|
|
||||||
public RSAFactoryParams()
|
public RSAFactoryParams()
|
||||||
{
|
{
|
||||||
_lazyInitializer = new(() =>
|
_lazyPbeParameters = new(() => new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount));
|
||||||
{
|
|
||||||
AfterCreate?.Invoke();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
AfterCreate += () => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected event Action AfterCreate;
|
|
||||||
|
|
||||||
public void Init() => _ = _lazyInitializer.Value;
|
|
||||||
|
|
||||||
public void OnDeserialized() => Init();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Abstractions.Security;
|
||||||
using DigitalData.Core.Security.Config;
|
using DigitalData.Core.Security.Config;
|
||||||
using DigitalData.Core.Security.RSAKey;
|
using DigitalData.Core.Security.RSAKey;
|
||||||
|
using DigitalData.Core.Security.Services;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@ -10,62 +11,27 @@ namespace DigitalData.Core.Security
|
|||||||
{
|
{
|
||||||
public static class DIExtensions
|
public static class DIExtensions
|
||||||
{
|
{
|
||||||
private static IServiceCollection AddParamsConfigureOptions<TParams>(this IServiceCollection services) where TParams : RSAFactoryParams
|
|
||||||
=> services.AddSingleton<IConfigureOptions<TParams>, ParamsConfigureOptions<TParams>>();
|
|
||||||
|
|
||||||
private static IServiceCollection AddCryptoFactory(this IServiceCollection services) => services
|
|
||||||
.AddParamsConfigureOptions<CryptoFactoryParams>()
|
|
||||||
.AddAutoMapper(typeof(MappingProfile).Assembly)
|
|
||||||
.AddSingleton<ICryptoFactory, CryptoFactory>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a custom asym crypt service with specified parameters from the given configuration section.
|
/// Registers a custom asym crypt service with specified parameters from the given configuration section.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services"></param>
|
/// <param name="services"></param>
|
||||||
/// <param name="section"></param>
|
/// <param name="section"></param>
|
||||||
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
||||||
public static IServiceCollection AddCryptoFactory(this IServiceCollection services, IConfigurationSection section) => services
|
public static IServiceCollection AddCryptoFactory(this IServiceCollection services, IConfiguration configuration) => services
|
||||||
.Configure<CryptoFactoryParams>(section)
|
.Configure<CryptoFactoryParams>(configuration)
|
||||||
.AddCryptoFactory();
|
.AddAutoMapper(typeof(MappingProfile).Assembly)
|
||||||
|
.AddSingleton<ICryptoFactory, CryptoFactory>()
|
||||||
/// <summary>
|
.AddHostedService<PemFileInitalizer>();
|
||||||
/// Registers an asym crypt service with the specified parameters from the given instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
|
||||||
public static IServiceCollection AddCryptoFactory(this IServiceCollection services, CryptoFactoryParams? factoryParams = null) => services
|
|
||||||
.AddSingleton(Options.Create(factoryParams ?? new()))
|
|
||||||
.AddCryptoFactory();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a custom RSA Factory with specified parameters from the given configuration section.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
/// <param name="section"></param>
|
|
||||||
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
|
||||||
public static IServiceCollection AddRSAFactory(this IServiceCollection services, IConfigurationSection section) => services
|
|
||||||
.AddParamsConfigureOptions<RSAFactoryParams>()
|
|
||||||
.Configure<RSAFactoryParams>(section)
|
|
||||||
.AddSingleton<IAsymmetricKeyFactory, RSAFactory<RSAFactoryParams>>();
|
|
||||||
|
|
||||||
private static IServiceCollection AddClaimDescriptor<TPrincipal>(this IServiceCollection services,
|
|
||||||
Func<TPrincipal, IDictionary<string, object>>? claimsMapper = null,
|
|
||||||
Func<TPrincipal, ClaimsIdentity>? subjectMapper = null)
|
|
||||||
{
|
|
||||||
var descriptor = new ClaimDescriptor<TPrincipal>
|
|
||||||
{
|
|
||||||
CreateClaims = claimsMapper,
|
|
||||||
CreateSubject = subjectMapper
|
|
||||||
};
|
|
||||||
|
|
||||||
return services.AddSingleton(sp => Options.Create(descriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddJwtSignatureHandler<TPrincipal>(this IServiceCollection services,
|
public static IServiceCollection AddJwtSignatureHandler<TPrincipal>(this IServiceCollection services,
|
||||||
Func<TPrincipal, IDictionary<string, object>>? claimsMapper = null,
|
Func<TPrincipal, IDictionary<string, object>>? claimsMapper = null,
|
||||||
Func<TPrincipal, ClaimsIdentity>? subjectMapper = null)
|
Func<TPrincipal, ClaimsIdentity>? subjectMapper = null)
|
||||||
=> services
|
=> services
|
||||||
.AddClaimDescriptor(claimsMapper: claimsMapper, subjectMapper: subjectMapper)
|
.AddSingleton<IJwtSignatureHandler<TPrincipal>, JwtSignatureHandler<TPrincipal>>()
|
||||||
.AddSingleton<IJwtSignatureHandler<TPrincipal>, JwtSignatureHandler<TPrincipal>>();
|
.AddSingleton(sp => Options.Create(new ClaimDescriptor<TPrincipal>
|
||||||
|
{
|
||||||
|
CreateClaims = claimsMapper,
|
||||||
|
CreateSubject = subjectMapper
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
|
|
||||||
public RSAFactory(IOptions<TRSAFactoryParams> options)
|
public RSAFactory(IOptions<TRSAFactoryParams> options)
|
||||||
{
|
{
|
||||||
options.Value.Init();
|
|
||||||
_params = options.Value;
|
_params = options.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
86
DigitalData.Core.Security/Services/PemFileInitalizer.cs
Normal file
86
DigitalData.Core.Security/Services/PemFileInitalizer.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using DigitalData.Core.Security.Config;
|
||||||
|
using DigitalData.Core.Security.RSAKey;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace DigitalData.Core.Security.Services;
|
||||||
|
|
||||||
|
public class PemFileInitalizer : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly CryptoFactoryParams _factoryParams;
|
||||||
|
|
||||||
|
private readonly ILogger<PemFileInitalizer>? _logger;
|
||||||
|
|
||||||
|
public PemFileInitalizer(IOptions<CryptoFactoryParams> factoryParamsOptions, ILogger<PemFileInitalizer>? logger = null)
|
||||||
|
{
|
||||||
|
_factoryParams = factoryParamsOptions.Value;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
if (_logger is null)
|
||||||
|
await InitPemFiles(stoppingToken);
|
||||||
|
else try
|
||||||
|
{
|
||||||
|
await InitPemFiles(stoppingToken);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Pem files cannot be initialized.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitPemFiles(CancellationToken stoppingToken = default)
|
||||||
|
{
|
||||||
|
// Create root folder if it does not exist
|
||||||
|
if (!Directory.Exists(_factoryParams.PemDirectory))
|
||||||
|
Directory.CreateDirectory(_factoryParams.PemDirectory);
|
||||||
|
|
||||||
|
var privateKeys = new List<RSAPrivateKey>();
|
||||||
|
privateKeys.AddRange(_factoryParams.Decryptors);
|
||||||
|
privateKeys.AddRange(_factoryParams.TokenDescriptors);
|
||||||
|
if (_factoryParams.VaultDecryptor is not null)
|
||||||
|
privateKeys.Add(_factoryParams.VaultDecryptor);
|
||||||
|
|
||||||
|
foreach (var privateKey in privateKeys)
|
||||||
|
{
|
||||||
|
// set default path
|
||||||
|
if (privateKey.IsPemNull)
|
||||||
|
{
|
||||||
|
// file name
|
||||||
|
var file_name_params = new List<object>();
|
||||||
|
|
||||||
|
if (privateKey.Id is not null)
|
||||||
|
file_name_params.Add(privateKey.Id);
|
||||||
|
else if (privateKey is RSATokenDescriptor descriptor)
|
||||||
|
file_name_params.Add(descriptor.Issuer);
|
||||||
|
|
||||||
|
file_name_params.Add(_factoryParams.KeySizeInBits);
|
||||||
|
file_name_params.Add(DateTime.Now.ToTag(_factoryParams.DateTagFormat));
|
||||||
|
|
||||||
|
if (privateKey.IsEncrypted)
|
||||||
|
file_name_params.Add(Secrets.Version);
|
||||||
|
|
||||||
|
var file_name = $"{string.Join(_factoryParams.FileNameSeparator, file_name_params)}.{_factoryParams.FileExtension}";
|
||||||
|
|
||||||
|
var path = Path.Combine(_factoryParams.PemDirectory, file_name);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
privateKey.SetPem(File.ReadAllText(path));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var pem = privateKey.IsEncrypted
|
||||||
|
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(pbeParameters: _factoryParams.PbeParameters, keySizeInBits: _factoryParams.KeySizeInBits, password: Secrets.PBE_PASSWORD)
|
||||||
|
: Instance.RSAFactory.CreatePrivateKeyPem(keySizeInBits: _factoryParams.KeySizeInBits);
|
||||||
|
|
||||||
|
privateKey.SetPem(pem);
|
||||||
|
|
||||||
|
// Save file in background
|
||||||
|
await File.WriteAllTextAsync(path: path, pem, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user