using System; using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using StellaOps.Cryptography; #if STELLAOPS_CRYPTO_PRO using StellaOps.Cryptography.Plugin.CryptoPro; #endif using StellaOps.Cryptography.Plugin.Pkcs11Gost; using StellaOps.Cryptography.Plugin.OpenSslGost; namespace StellaOps.Cryptography.DependencyInjection; /// /// Dependency injection helpers for registering StellaOps cryptography services. /// public static class CryptoServiceCollectionExtensions { /// /// Registers the default crypto provider and registry. /// /// Service collection. /// Optional registry ordering configuration. /// Optional provider-level configuration (e.g. key registration). /// The service collection. public static IServiceCollection AddStellaOpsCrypto( this IServiceCollection services, Action? configureRegistry = null, Action? configureProvider = null) { ArgumentNullException.ThrowIfNull(services); if (configureRegistry is not null) { services.Configure(configureRegistry); } // Register compliance options with default profile services.TryAddSingleton>(sp => { var configuration = sp.GetService(); var options = new CryptoComplianceOptions(); configuration?.GetSection(CryptoComplianceOptions.SectionKey).Bind(options); options.ApplyEnvironmentOverrides(); return new StaticComplianceOptionsMonitor(options); }); // Register compliance service services.TryAddSingleton(); services.TryAddSingleton(sp => { var provider = new DefaultCryptoProvider(); configureProvider?.Invoke(provider); return provider; }); services.TryAddEnumerable(ServiceDescriptor.Singleton()); #if STELLAOPS_CRYPTO_SODIUM services.TryAddSingleton(); services.TryAddEnumerable(ServiceDescriptor.Singleton()); #endif services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(sp => { var providers = sp.GetServices(); var options = sp.GetService>(); IEnumerable? preferred = options?.CurrentValue?.ResolvePreferredProviders(); return new CryptoProviderRegistry(providers, preferred); }); return services; } /// /// Registers crypto services with compliance profile configuration. /// /// Service collection. /// Configuration root. /// Optional compliance configuration. /// The service collection. public static IServiceCollection AddStellaOpsCryptoWithCompliance( this IServiceCollection services, IConfiguration configuration, Action? configureCompliance = null) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); // Bind compliance options from configuration services.Configure(options => { configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options); configureCompliance?.Invoke(options); options.ApplyEnvironmentOverrides(); }); // Register compliance service with options monitor services.TryAddSingleton(); // Register base crypto services services.AddStellaOpsCrypto(); return services; } /// /// Helper class for static options monitoring. /// private sealed class StaticComplianceOptionsMonitor : IOptionsMonitor { private readonly CryptoComplianceOptions _options; public StaticComplianceOptionsMonitor(CryptoComplianceOptions options) => _options = options; public CryptoComplianceOptions CurrentValue => _options; public CryptoComplianceOptions Get(string? name) => _options; public IDisposable OnChange(Action listener) => NullDisposable.Instance; private sealed class NullDisposable : IDisposable { public static readonly NullDisposable Instance = new(); public void Dispose() { } } } public static IServiceCollection AddStellaOpsCryptoRu( this IServiceCollection services, IConfiguration configuration, Action? configureRegistry = null) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); var baseSection = configuration.GetSection("StellaOps:Crypto"); services.Configure(baseSection); services.Configure(baseSection.GetSection("Registry")); #if STELLAOPS_CRYPTO_PRO services.Configure(baseSection.GetSection("CryptoPro")); #endif services.Configure(baseSection.GetSection("Pkcs11")); services.Configure(baseSection.GetSection("OpenSsl")); services.AddStellaOpsCrypto(configureRegistry); services.AddOpenSslGostProvider(); services.AddPkcs11GostProvider(); #if STELLAOPS_CRYPTO_PRO if (OperatingSystem.IsWindows()) { services.AddCryptoProGostProvider(); } #endif services.PostConfigure(options => { EnsurePreferred(options.PreferredProviders); foreach (var profile in options.Profiles.Values) { EnsurePreferred(profile.PreferredProviders); } }); return services; static void EnsurePreferred(IList providers) { InsertIfMissing(providers, "ru.pkcs11"); InsertIfMissing(providers, "ru.openssl.gost"); #if STELLAOPS_CRYPTO_PRO if (OperatingSystem.IsWindows()) { InsertIfMissing(providers, "ru.cryptopro.csp"); } #endif } static void InsertIfMissing(IList providers, string name) { for (var i = 0; i < providers.Count; i++) { if (string.Equals(providers[i], name, StringComparison.OrdinalIgnoreCase)) { return; } } providers.Insert(0, name); } } }