This commit is contained in:
master
2026-02-04 19:59:20 +02:00
parent 557feefdc3
commit 5548cf83bf
1479 changed files with 53557 additions and 40339 deletions

View File

@@ -0,0 +1,28 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoComplianceOptionsConfiguration : IConfigureOptions<CryptoComplianceOptions>
{
private readonly IConfiguration? _configuration;
public CryptoComplianceOptionsConfiguration(IConfiguration? configuration = null)
{
_configuration = configuration;
}
public void Configure(CryptoComplianceOptions options)
{
ArgumentNullException.ThrowIfNull(options);
if (_configuration is null)
{
return;
}
_configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
}
}

View File

@@ -0,0 +1,14 @@
using System;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoComplianceOptionsPostConfigure : IPostConfigureOptions<CryptoComplianceOptions>
{
public void PostConfigure(string? name, CryptoComplianceOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.ApplyEnvironmentOverrides();
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal static class CryptoComplianceOptionsRegistration
{
internal static void Register(IServiceCollection services, bool bindFromConfiguration)
{
services.AddOptions<CryptoComplianceOptions>();
if (bindFromConfiguration)
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<CryptoComplianceOptions>, CryptoComplianceOptionsConfiguration>());
}
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CryptoComplianceOptions>, CryptoComplianceOptionsPostConfigure>());
}
}

View File

@@ -0,0 +1,10 @@
using StellaOps.Cryptography.Plugin.SimRemote;
using StellaOps.Cryptography.Plugin.SmRemote;
namespace StellaOps.Cryptography.DependencyInjection;
internal static class CryptoHttpClientNames
{
internal const string SimRemote = nameof(SimRemoteHttpClient);
internal const string SmRemote = nameof(SmRemoteHttpClient);
}

View File

@@ -0,0 +1,15 @@
using System;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.PluginLoader;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginConfigurationOptions : IOptions<CryptoPluginConfiguration>
{
public CryptoPluginConfigurationOptions(CryptoPluginConfiguration configuration)
{
Value = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public CryptoPluginConfiguration Value { get; }
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Cryptography.PluginLoader;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginConfigurationRegistry : ICryptoProviderRegistry
{
private readonly CryptoProviderRegistry _registry;
public CryptoPluginConfigurationRegistry(
IReadOnlyList<ICryptoProvider> providers,
IOptions<CryptoPluginConfiguration> configuration,
ILogger<CryptoPluginConfigurationRegistry>? logger = null)
{
ArgumentNullException.ThrowIfNull(providers);
ArgumentNullException.ThrowIfNull(configuration);
var config = configuration.Value;
var preferredProviderNames = providers
.OrderByDescending(provider => GetProviderPriority(provider, config))
.Select(provider => provider.Name)
.ToList();
logger?.LogInformation(
"Loaded {Count} crypto provider(s) with preferred order: {Providers}",
providers.Count,
string.Join(", ", preferredProviderNames));
_registry = new CryptoProviderRegistry(providers, preferredProviderNames);
}
public IReadOnlyCollection<ICryptoProvider> Providers => _registry.Providers;
public bool TryResolve(string preferredProvider, out ICryptoProvider provider)
=> _registry.TryResolve(preferredProvider, out provider);
public ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId)
=> _registry.ResolveOrThrow(capability, algorithmId);
public CryptoSignerResolution ResolveSigner(
CryptoCapability capability,
string algorithmId,
CryptoKeyReference keyReference,
string? preferredProvider = null)
=> _registry.ResolveSigner(capability, algorithmId, keyReference, preferredProvider);
public CryptoHasherResolution ResolveHasher(string algorithmId, string? preferredProvider = null)
=> _registry.ResolveHasher(algorithmId, preferredProvider);
private static int GetProviderPriority(ICryptoProvider provider, CryptoPluginConfiguration config)
{
var enabledEntry = config.Enabled.FirstOrDefault(entry =>
entry.Id.Equals(provider.Name, StringComparison.OrdinalIgnoreCase));
return enabledEntry?.Priority ?? 50;
}
}

View File

@@ -0,0 +1,11 @@
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginDirectoryOptions
{
public CryptoPluginDirectoryOptions(string? directory)
{
Directory = directory;
}
public string? Directory { get; }
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginProviderEnumerable : IEnumerable<ICryptoProvider>
{
private readonly IReadOnlyList<ICryptoProvider> _providers;
public CryptoPluginProviderEnumerable(IReadOnlyList<ICryptoProvider> providers)
{
_providers = providers ?? throw new ArgumentNullException(nameof(providers));
}
public IEnumerator<ICryptoProvider> GetEnumerator() => _providers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Cryptography.PluginLoader;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginProviderList : IReadOnlyList<ICryptoProvider>
{
private readonly IReadOnlyList<ICryptoProvider> _providers;
public CryptoPluginProviderList(
IOptions<CryptoPluginConfiguration> configuration,
ILogger<CryptoPluginLoader>? logger = null,
CryptoPluginDirectoryOptions? directory = null)
{
ArgumentNullException.ThrowIfNull(configuration);
var loader = new CryptoPluginLoader(configuration.Value, logger, directory?.Directory);
try
{
_providers = loader.LoadProviders();
}
catch (CryptoPluginLoadException ex)
{
logger?.LogCritical(ex, "Failed to load crypto plugins: {Message}", ex.Message);
throw;
}
if (_providers.Count == 0)
{
throw new InvalidOperationException(
"No crypto providers were loaded. Check plugin configuration and manifest.");
}
}
public int Count => _providers.Count;
public ICryptoProvider this[int index] => _providers[index];
public IEnumerator<ICryptoProvider> GetEnumerator() => _providers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class CryptoPluginProviderRegistry : ICryptoProviderRegistry
{
private readonly CryptoProviderRegistry _registry;
public CryptoPluginProviderRegistry(
IReadOnlyList<ICryptoProvider> providers,
IOptionsMonitor<CryptoProviderRegistryOptions>? optionsMonitor = null)
{
ArgumentNullException.ThrowIfNull(providers);
var preferred = optionsMonitor?.CurrentValue?.ResolvePreferredProviders();
_registry = new CryptoProviderRegistry(providers, preferred);
}
public IReadOnlyCollection<ICryptoProvider> Providers => _registry.Providers;
public bool TryResolve(string preferredProvider, out ICryptoProvider provider)
=> _registry.TryResolve(preferredProvider, out provider);
public ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId)
=> _registry.ResolveOrThrow(capability, algorithmId);
public CryptoSignerResolution ResolveSigner(
CryptoCapability capability,
string algorithmId,
CryptoKeyReference keyReference,
string? preferredProvider = null)
=> _registry.ResolveSigner(capability, algorithmId, keyReference, preferredProvider);
public CryptoHasherResolution ResolveHasher(string algorithmId, string? preferredProvider = null)
=> _registry.ResolveHasher(algorithmId, preferredProvider);
}

View File

@@ -0,0 +1,37 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cryptography;
using StellaOps.Cryptography.PluginLoader;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoPluginServiceCollectionExtensions
{
/// <summary>
/// Registers crypto providers with plugin loading and compliance profile configuration.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Application configuration.</param>
/// <param name="configurePlugins">Optional plugin configuration.</param>
/// <param name="configureCompliance">Optional compliance configuration.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoWithPluginsAndCompliance(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoPluginConfiguration>? configurePlugins = null,
Action<CryptoComplianceOptions>? configureCompliance = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
services.AddStellaOpsCryptoWithPlugins(configuration, configurePlugins);
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
configureCompliance?.Invoke(options);
});
return services;
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Cryptography.PluginLoader;
@@ -11,7 +11,7 @@ namespace StellaOps.Cryptography.DependencyInjection;
/// <summary>
/// DI extension methods for configuration-driven crypto plugin loading.
/// </summary>
public static class CryptoPluginServiceCollectionExtensions
public static partial class CryptoPluginServiceCollectionExtensions
{
/// <summary>
/// Registers crypto providers using configuration-driven plugin loading.
@@ -29,112 +29,25 @@ public static class CryptoPluginServiceCollectionExtensions
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Bind plugin configuration from appsettings
CryptoComplianceOptionsRegistration.Register(services, bindFromConfiguration: false);
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
});
services.Configure<CryptoPluginConfiguration>(options =>
{
configuration.GetSection("StellaOps:Crypto:Plugins").Bind(options);
configurePlugins?.Invoke(options);
});
// Register compliance options (reuse existing code)
services.TryAddSingleton<IOptionsMonitor<CryptoComplianceOptions>>(sp =>
{
var config = sp.GetService<IConfiguration>();
var options = new CryptoComplianceOptions();
config?.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
options.ApplyEnvironmentOverrides();
return new StaticComplianceOptionsMonitor(options);
});
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
services.TryAddSingleton<ICryptoHash, DefaultCryptoHash>();
services.TryAddSingleton<ICryptoHmac, DefaultCryptoHmac>();
// Register plugin loader and load providers dynamically
services.TryAddSingleton<CryptoPluginLoader>(sp =>
{
var pluginConfig = sp.GetRequiredService<IOptions<CryptoPluginConfiguration>>().Value;
var logger = sp.GetService<ILogger<CryptoPluginLoader>>();
return new CryptoPluginLoader(pluginConfig, logger);
});
// Load all configured crypto providers
services.TryAddSingleton(sp =>
{
var loader = sp.GetRequiredService<CryptoPluginLoader>();
return loader.LoadProviders();
});
// Register each loaded provider as ICryptoProvider
services.TryAddSingleton<IEnumerable<ICryptoProvider>>(sp =>
{
return sp.GetRequiredService<IReadOnlyList<ICryptoProvider>>();
});
// Register crypto provider registry with loaded providers
services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
{
var providers = sp.GetRequiredService<IReadOnlyList<ICryptoProvider>>();
var options = sp.GetService<IOptionsMonitor<CryptoProviderRegistryOptions>>();
IEnumerable<string>? preferred = options?.CurrentValue?.ResolvePreferredProviders();
return new CryptoProviderRegistry(providers, preferred);
});
services.TryAddSingleton<IReadOnlyList<ICryptoProvider>, CryptoPluginProviderList>();
services.TryAddSingleton<IEnumerable<ICryptoProvider>, CryptoPluginProviderEnumerable>();
services.TryAddSingleton<ICryptoProviderRegistry, CryptoPluginProviderRegistry>();
return services;
}
/// <summary>
/// Registers crypto providers with plugin loading and compliance profile configuration.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Application configuration.</param>
/// <param name="configurePlugins">Optional plugin configuration.</param>
/// <param name="configureCompliance">Optional compliance configuration.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoWithPluginsAndCompliance(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoPluginConfiguration>? configurePlugins = null,
Action<CryptoComplianceOptions>? configureCompliance = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Bind compliance options from configuration
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
configureCompliance?.Invoke(options);
options.ApplyEnvironmentOverrides();
});
// Register base crypto services with plugin loading
services.AddStellaOpsCryptoWithPlugins(configuration, configurePlugins);
return services;
}
/// <summary>
/// Helper class for static options monitoring.
/// </summary>
private sealed class StaticComplianceOptionsMonitor : IOptionsMonitor<CryptoComplianceOptions>
{
private readonly CryptoComplianceOptions _options;
public StaticComplianceOptionsMonitor(CryptoComplianceOptions options)
=> _options = options;
public CryptoComplianceOptions CurrentValue => _options;
public CryptoComplianceOptions Get(string? name) => _options;
public IDisposable OnChange(Action<CryptoComplianceOptions, string> listener)
=> NullDisposable.Instance;
private sealed class NullDisposable : IDisposable
{
public static readonly NullDisposable Instance = new();
public void Dispose() { }
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace StellaOps.Cryptography.DependencyInjection;
public sealed class CryptoProviderProfileOptions
{
/// <summary>
/// Ordered list of preferred provider names for the profile.
/// </summary>
public IList<string> PreferredProviders { get; } = new List<string>();
}

View File

@@ -74,11 +74,3 @@ public sealed class CryptoProviderRegistryOptions
return Array.Empty<string>();
}
}
public sealed class CryptoProviderProfileOptions
{
/// <summary>
/// Ordered list of preferred provider names for the profile.
/// </summary>
public IList<string> PreferredProviders { get; } = new List<string>();
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoProviderRegistryValidator
{
private static bool GetEnvFlag(string name, bool defaultValue)
{
var raw = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrWhiteSpace(raw))
{
return defaultValue;
}
return raw.Equals("1", StringComparison.OrdinalIgnoreCase) ||
raw.Equals("true", StringComparison.OrdinalIgnoreCase) ||
raw.Equals("yes", StringComparison.OrdinalIgnoreCase);
}
private static void EnsureBaselineProfiles(CryptoProviderRegistryOptions options)
{
if (!options.PreferredProviders.Any())
{
options.PreferredProviders.Add("default");
}
if (!options.Profiles.TryGetValue("ru-offline", out var ruOffline))
{
ruOffline = new CryptoProviderProfileOptions();
options.Profiles["ru-offline"] = ruOffline;
}
if (!options.Profiles.ContainsKey("ru-linux-soft"))
{
options.Profiles["ru-linux-soft"] = new CryptoProviderProfileOptions();
}
}
private static void EnsureDefaultPreferred(
IList<string> providers,
bool enableOpenSsl,
bool enablePkcs11,
bool enableWineCsp
#if STELLAOPS_CRYPTO_PRO
, bool enableCryptoPro
#endif
)
{
InsertIfMissing(providers, "default");
if (enableOpenSsl)
{
InsertIfMissing(providers, "ru.openssl.gost");
}
if (enablePkcs11)
{
InsertIfMissing(providers, "ru.pkcs11");
}
if (enableWineCsp)
{
InsertIfMissing(providers, "ru.winecsp.http");
}
#if STELLAOPS_CRYPTO_PRO
if (enableCryptoPro && OperatingSystem.IsWindows())
{
InsertIfMissing(providers, "ru.cryptopro.csp");
}
#endif
}
private static void InsertIfMissing(IList<string> providers, string name)
{
for (var i = 0; i < providers.Count; i++)
{
if (string.Equals(providers[i], name, StringComparison.OrdinalIgnoreCase))
{
return;
}
}
providers.Insert(0, name);
}
}

View File

@@ -7,9 +7,9 @@ namespace StellaOps.Cryptography.DependencyInjection;
/// <summary>
/// Validates and normalises crypto provider registry options for RU/GOST baselines.
/// </summary>
public static class CryptoProviderRegistryValidator
public static partial class CryptoProviderRegistryValidator
{
private static readonly StringComparer OrdinalIgnoreCase = StringComparer.OrdinalIgnoreCase;
private static readonly StringComparer _ordinalIgnoreCase = StringComparer.OrdinalIgnoreCase;
public static void EnforceRuLinuxDefaults(CryptoProviderRegistryOptions options)
{
@@ -50,7 +50,7 @@ public static class CryptoProviderRegistryValidator
}
if (OperatingSystem.IsLinux() && enableOpenSsl &&
!resolved.Contains("ru.openssl.gost", OrdinalIgnoreCase))
!resolved.Contains("ru.openssl.gost", _ordinalIgnoreCase))
{
throw new InvalidOperationException("Linux RU baseline requires provider 'ru.openssl.gost' (set STELLAOPS_CRYPTO_ENABLE_RU_OPENSSL=0 to override explicitly).");
}
@@ -60,84 +60,4 @@ public static class CryptoProviderRegistryValidator
throw new InvalidOperationException("RU Linux baseline is misconfigured: both ru.openssl.gost and ru.pkcs11 are disabled via environment. Enable at least one provider.");
}
}
private static bool GetEnvFlag(string name, bool defaultValue)
{
var raw = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrWhiteSpace(raw))
{
return defaultValue;
}
return raw.Equals("1", StringComparison.OrdinalIgnoreCase) ||
raw.Equals("true", StringComparison.OrdinalIgnoreCase) ||
raw.Equals("yes", StringComparison.OrdinalIgnoreCase);
}
private static void EnsureBaselineProfiles(CryptoProviderRegistryOptions options)
{
if (!options.PreferredProviders.Any())
{
options.PreferredProviders.Add("default");
}
if (!options.Profiles.TryGetValue("ru-offline", out var ruOffline))
{
ruOffline = new CryptoProviderProfileOptions();
options.Profiles["ru-offline"] = ruOffline;
}
if (!options.Profiles.ContainsKey("ru-linux-soft"))
{
options.Profiles["ru-linux-soft"] = new CryptoProviderProfileOptions();
}
}
private static void EnsureDefaultPreferred(
IList<string> providers,
bool enableOpenSsl,
bool enablePkcs11,
bool enableWineCsp
#if STELLAOPS_CRYPTO_PRO
, bool enableCryptoPro
#endif
)
{
InsertIfMissing(providers, "default");
if (enableOpenSsl)
{
InsertIfMissing(providers, "ru.openssl.gost");
}
if (enablePkcs11)
{
InsertIfMissing(providers, "ru.pkcs11");
}
if (enableWineCsp)
{
InsertIfMissing(providers, "ru.winecsp.http");
}
#if STELLAOPS_CRYPTO_PRO
if (enableCryptoPro && OperatingSystem.IsWindows())
{
InsertIfMissing(providers, "ru.cryptopro.csp");
}
#endif
}
private static void InsertIfMissing(IList<string> providers, string name)
{
for (var i = 0; i < providers.Count; i++)
{
if (string.Equals(providers[i], name, StringComparison.OrdinalIgnoreCase))
{
return;
}
}
providers.Insert(0, name);
}
}

View File

@@ -0,0 +1,35 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
/// <summary>
/// Registers crypto services with compliance profile configuration.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="configureCompliance">Optional compliance configuration.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoWithCompliance(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoComplianceOptions>? configureCompliance = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
services.AddStellaOpsCrypto();
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
configureCompliance?.Invoke(options);
});
return services;
}
}

View File

@@ -0,0 +1,37 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Cryptography;
using StellaOps.Cryptography.Plugin.PqSoft;
using StellaOps.Cryptography.Plugin.SmRemote;
using StellaOps.Cryptography.Plugin.SmSoft;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
private static void RegisterDefaultProviders(
IServiceCollection services,
Action<DefaultCryptoProvider>? configureProvider)
{
var defaultProvider = new DefaultCryptoProvider();
configureProvider?.Invoke(defaultProvider);
services.TryAddSingleton(defaultProvider);
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider>(defaultProvider));
#if STELLAOPS_CRYPTO_SODIUM
services.TryAddSingleton<LibsodiumCryptoProvider>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, LibsodiumCryptoProvider>());
#endif
services.TryAddSingleton<ICryptoHash, DefaultCryptoHash>();
services.TryAddSingleton<ICryptoHmac, DefaultCryptoHmac>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SmSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SmRemoteHttpProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, PqSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, FipsSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, EidasSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, KcmvpHashOnlyProvider>());
}
}

View File

@@ -0,0 +1,84 @@
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;
using StellaOps.Cryptography.PluginLoader;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
/// <summary>
/// Registers crypto services using configuration-driven plugin loading.
/// This is the recommended method for production deployments with regional compliance requirements.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="pluginDirectory">Optional custom plugin directory path. Defaults to application base directory.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoFromConfiguration(
this IServiceCollection services,
IConfiguration configuration,
string? pluginDirectory = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var pluginConfig = new CryptoPluginConfiguration();
configuration.GetSection("StellaOps:Crypto:Plugins").Bind(pluginConfig);
var complianceConfig = new CryptoComplianceConfiguration();
configuration.GetSection("StellaOps:Crypto:Compliance").Bind(complianceConfig);
pluginConfig.Compliance = complianceConfig;
services.AddSingleton(pluginConfig);
services.TryAddSingleton<IOptions<CryptoPluginConfiguration>, CryptoPluginConfigurationOptions>();
services.TryAddSingleton(new CryptoPluginDirectoryOptions(pluginDirectory));
CryptoComplianceOptionsRegistration.Register(services, bindFromConfiguration: false);
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
});
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
services.TryAddSingleton<IReadOnlyList<ICryptoProvider>, CryptoPluginProviderList>();
services.TryAddSingleton<IEnumerable<ICryptoProvider>, CryptoPluginProviderEnumerable>();
services.TryAddSingleton<ICryptoProviderRegistry, CryptoPluginConfigurationRegistry>();
return services;
}
/// <summary>
/// Registers crypto services using configuration-driven plugin loading with explicit compliance profile.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="complianceProfileId">Compliance profile identifier (e.g., "gost", "fips", "eidas", "sm").</param>
/// <param name="strictValidation">Enable strict compliance validation.</param>
/// <param name="pluginDirectory">Optional custom plugin directory path.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoFromConfiguration(
this IServiceCollection services,
IConfiguration configuration,
string complianceProfileId,
bool strictValidation = true,
string? pluginDirectory = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
ArgumentNullException.ThrowIfNull(complianceProfileId);
services.AddStellaOpsCryptoFromConfiguration(configuration, pluginDirectory);
services.Configure<CryptoComplianceOptions>(options =>
{
options.ProfileId = complianceProfileId;
options.StrictValidation = strictValidation;
});
return services;
}
}

View File

@@ -0,0 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
private static void RegisterRegistry(IServiceCollection services)
{
services.TryAddSingleton<ICryptoProviderRegistry, ServiceRegisteredCryptoProviderRegistry>();
}
}

View File

@@ -0,0 +1,48 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cryptography.Plugin.OpenSslGost;
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
using StellaOps.Cryptography.Plugin.WineCsp;
#if STELLAOPS_CRYPTO_PRO
using StellaOps.Cryptography.Plugin.CryptoPro;
#endif
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
public static IServiceCollection AddStellaOpsCryptoRu(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoProviderRegistryOptions>? configureRegistry = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var baseSection = configuration.GetSection("StellaOps:Crypto");
services.Configure<StellaOpsCryptoOptions>(baseSection);
services.Configure<CryptoProviderRegistryOptions>(baseSection.GetSection("Registry"));
#if STELLAOPS_CRYPTO_PRO
services.Configure<CryptoProGostProviderOptions>(baseSection.GetSection("CryptoPro"));
#endif
services.Configure<Pkcs11GostProviderOptions>(baseSection.GetSection("Pkcs11"));
services.Configure<OpenSslGostProviderOptions>(baseSection.GetSection("OpenSsl"));
services.Configure<WineCspProviderOptions>(baseSection.GetSection("WineCsp"));
services.AddStellaOpsCrypto(configureRegistry);
services.AddOpenSslGostProvider();
services.AddPkcs11GostProvider();
services.AddWineCspProvider();
#if STELLAOPS_CRYPTO_PRO
if (OperatingSystem.IsWindows())
{
services.AddCryptoProGostProvider();
}
#endif
services.PostConfigure<CryptoProviderRegistryOptions>(CryptoProviderRegistryValidator.EnforceRuLinuxDefaults);
return services;
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Cryptography.Plugin.SimRemote;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
private static void RegisterSimRemote(IServiceCollection services)
{
services.AddOptions<SimRemoteProviderOptions>();
services.AddHttpClient<SimRemoteHttpClient>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<SimRemoteProviderOptions>, SimRemoteProviderOptionsConfiguration>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<SimRemoteProviderOptions>, SimRemoteProviderOptionsPostConfigure>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<HttpClientFactoryOptions>, SimRemoteHttpClientOptionsConfiguration>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SimRemoteProvider>());
}
private static void RegisterSimRemoteRegistryProfile(IServiceCollection services)
{
services.PostConfigure<CryptoProviderRegistryOptions>(options =>
{
var enableSimEnv = Environment.GetEnvironmentVariable("STELLAOPS_CRYPTO_ENABLE_SIM");
var enableSim = string.Equals(enableSimEnv, "1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(enableSimEnv, "true", StringComparison.OrdinalIgnoreCase);
if (!enableSim)
{
return;
}
void AddIfMissing(IList<string> list, string provider)
{
if (!list.Contains(provider, StringComparer.OrdinalIgnoreCase))
{
list.Add(provider);
}
}
if (!string.IsNullOrWhiteSpace(options.ActiveProfile) &&
options.Profiles.TryGetValue(options.ActiveProfile, out var profile))
{
AddIfMissing(profile.PreferredProviders, "sim.crypto.remote");
}
else
{
AddIfMissing(options.PreferredProviders, "sim.crypto.remote");
}
});
}
}

View File

@@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.SmRemote;
namespace StellaOps.Cryptography.DependencyInjection;
public static partial class CryptoServiceCollectionExtensions
{
private static void RegisterSmRemote(IServiceCollection services)
{
services.AddOptions<SmRemoteProviderOptions>();
services.AddHttpClient<SmRemoteHttpClient>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<HttpClientFactoryOptions>, SmRemoteHttpClientOptionsConfiguration>());
}
}

View File

@@ -1,29 +1,13 @@
using StellaOps.Cryptography.Plugin.SimRemote;
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;
using StellaOps.Cryptography.PluginLoader;
#if STELLAOPS_CRYPTO_PRO
using StellaOps.Cryptography.Plugin.CryptoPro;
#endif
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
using StellaOps.Cryptography.Plugin.OpenSslGost;
using StellaOps.Cryptography.Plugin.SmRemote;
using StellaOps.Cryptography.Plugin.SmSoft;
using StellaOps.Cryptography.Plugin.PqSoft;
using StellaOps.Cryptography.Plugin.WineCsp;
using Microsoft.Extensions.Logging;
namespace StellaOps.Cryptography.DependencyInjection;
/// <summary>
/// Dependency injection helpers for registering StellaOps cryptography services.
/// </summary>
public static class CryptoServiceCollectionExtensions
public static partial class CryptoServiceCollectionExtensions
{
/// <summary>
/// Registers the default crypto provider and registry.
@@ -39,328 +23,19 @@ public static class CryptoServiceCollectionExtensions
{
ArgumentNullException.ThrowIfNull(services);
CryptoComplianceOptionsRegistration.Register(services, bindFromConfiguration: true);
if (configureRegistry is not null)
{
services.Configure(configureRegistry);
}
// Register compliance options with default profile
services.TryAddSingleton<IOptionsMonitor<CryptoComplianceOptions>>(sp =>
{
var configuration = sp.GetService<IConfiguration>();
var options = new CryptoComplianceOptions();
configuration?.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
options.ApplyEnvironmentOverrides();
return new StaticComplianceOptionsMonitor(options);
});
// Register compliance service
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
services.TryAddSingleton<DefaultCryptoProvider>(sp =>
{
var provider = new DefaultCryptoProvider();
configureProvider?.Invoke(provider);
return provider;
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, DefaultCryptoProvider>());
#if STELLAOPS_CRYPTO_SODIUM
services.TryAddSingleton<LibsodiumCryptoProvider>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, LibsodiumCryptoProvider>());
#endif
services.TryAddSingleton<ICryptoHash, DefaultCryptoHash>();
services.TryAddSingleton<ICryptoHmac, DefaultCryptoHmac>();
services.AddOptions<SmRemoteProviderOptions>();
services.AddHttpClient<SmRemoteHttpClient>((sp, httpClient) =>
{
var opts = sp.GetService<IOptions<SmRemoteProviderOptions>>()?.Value;
if (opts is not null && !string.IsNullOrWhiteSpace(opts.BaseAddress))
{
httpClient.BaseAddress = new Uri(opts.BaseAddress);
}
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SmSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, StellaOps.Cryptography.Plugin.SmRemote.SmRemoteHttpProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, PqSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, FipsSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, EidasSoftCryptoProvider>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, KcmvpHashOnlyProvider>());
// Unified simulation provider (sim-crypto-service)
services.AddOptions<SimRemoteProviderOptions>()
.Configure<IConfiguration>((opts, config) =>
{
config?.GetSection("StellaOps:Crypto:Sim").Bind(opts);
})
.PostConfigure(opts =>
{
var simUrl = Environment.GetEnvironmentVariable("STELLAOPS_CRYPTO_SIM_URL");
if (!string.IsNullOrWhiteSpace(simUrl))
{
opts.BaseAddress = simUrl;
}
});
services.AddHttpClient<SimRemoteHttpClient>((sp, httpClient) =>
{
var opts = sp.GetService<IOptions<SimRemoteProviderOptions>>()?.Value;
if (opts is not null && !string.IsNullOrWhiteSpace(opts.BaseAddress))
{
httpClient.BaseAddress = new Uri(opts.BaseAddress);
}
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SimRemoteProvider>());
services.PostConfigure<CryptoProviderRegistryOptions>(opts =>
{
var enableSimEnv = Environment.GetEnvironmentVariable("STELLAOPS_CRYPTO_ENABLE_SIM");
var enableSim = string.Equals(enableSimEnv, "1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(enableSimEnv, "true", StringComparison.OrdinalIgnoreCase);
if (!enableSim)
{
return;
}
void AddIfMissing(IList<string> list, string provider)
{
if (!list.Contains(provider, StringComparer.OrdinalIgnoreCase))
{
list.Add(provider);
}
}
if (!string.IsNullOrWhiteSpace(opts.ActiveProfile) &&
opts.Profiles.TryGetValue(opts.ActiveProfile, out var profile))
{
AddIfMissing(profile.PreferredProviders, "sim.crypto.remote");
}
else
{
AddIfMissing(opts.PreferredProviders, "sim.crypto.remote");
}
});
services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
{
var providers = sp.GetServices<ICryptoProvider>();
var options = sp.GetService<IOptionsMonitor<CryptoProviderRegistryOptions>>();
IEnumerable<string>? preferred = options?.CurrentValue?.ResolvePreferredProviders();
return new CryptoProviderRegistry(providers, preferred);
});
RegisterDefaultProviders(services, configureProvider);
RegisterSmRemote(services);
RegisterSimRemote(services);
RegisterSimRemoteRegistryProfile(services);
RegisterRegistry(services);
return services;
}
/// <summary>
/// Registers crypto services with compliance profile configuration.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="configureCompliance">Optional compliance configuration.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoWithCompliance(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoComplianceOptions>? configureCompliance = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Bind compliance options from configuration
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
configureCompliance?.Invoke(options);
options.ApplyEnvironmentOverrides();
});
// Register compliance service with options monitor
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
// Register base crypto services
services.AddStellaOpsCrypto();
return services;
}
/// <summary>
/// Helper class for static options monitoring.
/// </summary>
private sealed class StaticComplianceOptionsMonitor : IOptionsMonitor<CryptoComplianceOptions>
{
private readonly CryptoComplianceOptions _options;
public StaticComplianceOptionsMonitor(CryptoComplianceOptions options)
=> _options = options;
public CryptoComplianceOptions CurrentValue => _options;
public CryptoComplianceOptions Get(string? name) => _options;
public IDisposable OnChange(Action<CryptoComplianceOptions, string> 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<CryptoProviderRegistryOptions>? configureRegistry = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var baseSection = configuration.GetSection("StellaOps:Crypto");
services.Configure<StellaOpsCryptoOptions>(baseSection);
services.Configure<CryptoProviderRegistryOptions>(baseSection.GetSection("Registry"));
#if STELLAOPS_CRYPTO_PRO
services.Configure<CryptoProGostProviderOptions>(baseSection.GetSection("CryptoPro"));
#endif
services.Configure<Pkcs11GostProviderOptions>(baseSection.GetSection("Pkcs11"));
services.Configure<OpenSslGostProviderOptions>(baseSection.GetSection("OpenSsl"));
services.Configure<WineCspProviderOptions>(baseSection.GetSection("WineCsp"));
services.AddStellaOpsCrypto(configureRegistry);
services.AddOpenSslGostProvider();
services.AddPkcs11GostProvider();
services.AddWineCspProvider();
#if STELLAOPS_CRYPTO_PRO
if (OperatingSystem.IsWindows())
{
services.AddCryptoProGostProvider();
}
#endif
services.PostConfigure<CryptoProviderRegistryOptions>(CryptoProviderRegistryValidator.EnforceRuLinuxDefaults);
return services;
}
/// <summary>
/// Registers crypto services using configuration-driven plugin loading.
/// This is the recommended method for production deployments with regional compliance requirements.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="pluginDirectory">Optional custom plugin directory path. Defaults to application base directory.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoFromConfiguration(
this IServiceCollection services,
IConfiguration configuration,
string? pluginDirectory = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Bind plugin configuration from appsettings
var pluginConfig = new CryptoPluginConfiguration();
configuration.GetSection("StellaOps:Crypto:Plugins").Bind(pluginConfig);
// Bind compliance configuration
var complianceConfig = new CryptoComplianceConfiguration();
configuration.GetSection("StellaOps:Crypto:Compliance").Bind(complianceConfig);
pluginConfig.Compliance = complianceConfig;
// Register plugin configuration as singleton
services.AddSingleton(pluginConfig);
// Register compliance options with configuration binding
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
options.ApplyEnvironmentOverrides();
});
// Register compliance service
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
// Load crypto providers using plugin loader
services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
{
var logger = sp.GetService<ILoggerFactory>()?.CreateLogger<CryptoPluginLoader>();
var loader = new CryptoPluginLoader(pluginConfig, logger, pluginDirectory);
IReadOnlyList<ICryptoProvider> providers;
try
{
providers = loader.LoadProviders();
}
catch (CryptoPluginLoadException ex)
{
logger?.LogCritical(ex, "Failed to load crypto plugins: {Message}", ex.Message);
throw;
}
if (providers.Count == 0)
{
throw new InvalidOperationException(
"No crypto providers were loaded. Check plugin configuration and manifest.");
}
// Extract provider names for preferred ordering (uses priority from manifest/config)
var preferredProviderNames = providers
.OrderByDescending(p => GetProviderPriority(p, pluginConfig))
.Select(p => p.Name)
.ToList();
logger?.LogInformation(
"Loaded {Count} crypto provider(s) with preferred order: {Providers}",
providers.Count,
string.Join(", ", preferredProviderNames));
return new CryptoProviderRegistry(providers, preferredProviderNames);
});
return services;
}
/// <summary>
/// Registers crypto services using configuration-driven plugin loading with explicit compliance profile.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="complianceProfileId">Compliance profile identifier (e.g., "gost", "fips", "eidas", "sm").</param>
/// <param name="strictValidation">Enable strict compliance validation.</param>
/// <param name="pluginDirectory">Optional custom plugin directory path.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoFromConfiguration(
this IServiceCollection services,
IConfiguration configuration,
string complianceProfileId,
bool strictValidation = true,
string? pluginDirectory = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
ArgumentNullException.ThrowIfNull(complianceProfileId);
// Override compliance configuration with explicit profile
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
options.ProfileId = complianceProfileId;
options.StrictValidation = strictValidation;
options.ApplyEnvironmentOverrides();
});
return services.AddStellaOpsCryptoFromConfiguration(configuration, pluginDirectory);
}
private static int GetProviderPriority(ICryptoProvider provider, CryptoPluginConfiguration config)
{
// Check if priority was overridden in configuration
var enabledEntry = config.Enabled.FirstOrDefault(e =>
e.Id.Equals(provider.Name, StringComparison.OrdinalIgnoreCase));
return enabledEntry?.Priority ?? 50; // Default priority
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class ServiceRegisteredCryptoProviderRegistry : ICryptoProviderRegistry
{
private readonly CryptoProviderRegistry _registry;
public ServiceRegisteredCryptoProviderRegistry(
IEnumerable<ICryptoProvider> providers,
IOptionsMonitor<CryptoProviderRegistryOptions>? optionsMonitor = null)
{
ArgumentNullException.ThrowIfNull(providers);
var preferred = optionsMonitor?.CurrentValue?.ResolvePreferredProviders();
_registry = new CryptoProviderRegistry(providers, preferred);
}
public IReadOnlyCollection<ICryptoProvider> Providers => _registry.Providers;
public bool TryResolve(string preferredProvider, out ICryptoProvider provider)
=> _registry.TryResolve(preferredProvider, out provider);
public ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId)
=> _registry.ResolveOrThrow(capability, algorithmId);
public CryptoSignerResolution ResolveSigner(
CryptoCapability capability,
string algorithmId,
CryptoKeyReference keyReference,
string? preferredProvider = null)
=> _registry.ResolveSigner(capability, algorithmId, keyReference, preferredProvider);
public CryptoHasherResolution ResolveHasher(string algorithmId, string? preferredProvider = null)
=> _registry.ResolveHasher(algorithmId, preferredProvider);
}

View File

@@ -0,0 +1,38 @@
using System;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.SimRemote;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class SimRemoteHttpClientOptionsConfiguration : IConfigureNamedOptions<HttpClientFactoryOptions>
{
private readonly IOptionsMonitor<SimRemoteProviderOptions> _options;
public SimRemoteHttpClientOptionsConfiguration(IOptionsMonitor<SimRemoteProviderOptions> options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public void Configure(HttpClientFactoryOptions options)
{
Configure(Options.DefaultName, options);
}
public void Configure(string? name, HttpClientFactoryOptions options)
{
if (!string.Equals(name, CryptoHttpClientNames.SimRemote, StringComparison.Ordinal))
{
return;
}
options.HttpClientActions.Add(client =>
{
var current = _options.CurrentValue;
if (!string.IsNullOrWhiteSpace(current.BaseAddress))
{
client.BaseAddress = new Uri(current.BaseAddress);
}
});
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.SimRemote;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class SimRemoteProviderOptionsConfiguration : IConfigureOptions<SimRemoteProviderOptions>
{
private readonly IConfiguration? _configuration;
public SimRemoteProviderOptionsConfiguration(IConfiguration? configuration = null)
{
_configuration = configuration;
}
public void Configure(SimRemoteProviderOptions options)
{
ArgumentNullException.ThrowIfNull(options);
if (_configuration is null)
{
return;
}
_configuration.GetSection("StellaOps:Crypto:Sim").Bind(options);
}
}

View File

@@ -0,0 +1,19 @@
using System;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.SimRemote;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class SimRemoteProviderOptionsPostConfigure : IPostConfigureOptions<SimRemoteProviderOptions>
{
public void PostConfigure(string? name, SimRemoteProviderOptions options)
{
ArgumentNullException.ThrowIfNull(options);
var simUrl = Environment.GetEnvironmentVariable("STELLAOPS_CRYPTO_SIM_URL");
if (!string.IsNullOrWhiteSpace(simUrl))
{
options.BaseAddress = simUrl;
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.SmRemote;
namespace StellaOps.Cryptography.DependencyInjection;
internal sealed class SmRemoteHttpClientOptionsConfiguration : IConfigureNamedOptions<HttpClientFactoryOptions>
{
private readonly IOptionsMonitor<SmRemoteProviderOptions> _options;
public SmRemoteHttpClientOptionsConfiguration(IOptionsMonitor<SmRemoteProviderOptions> options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public void Configure(HttpClientFactoryOptions options)
{
Configure(Options.DefaultName, options);
}
public void Configure(string? name, HttpClientFactoryOptions options)
{
if (!string.Equals(name, CryptoHttpClientNames.SmRemote, StringComparison.Ordinal))
{
return;
}
options.HttpClientActions.Add(client =>
{
var current = _options.CurrentValue;
if (!string.IsNullOrWhiteSpace(current.BaseAddress))
{
client.BaseAddress = new Uri(current.BaseAddress);
}
});
}
}

View File

@@ -1,8 +1,8 @@
using StellaOps.Cryptography.Plugin.OpenSslGost;
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
#if STELLAOPS_CRYPTO_PRO
using StellaOps.Cryptography.Plugin.CryptoPro;
#endif
using StellaOps.Cryptography.Plugin.OpenSslGost;
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
namespace StellaOps.Cryptography.DependencyInjection;

View File

@@ -9,3 +9,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0050-T | DONE | Revalidated 2026-01-08. |
| AUDIT-0050-A | TODO | Revalidated 2026-01-08 (open findings). |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| REMED-05 | DONE | Removed service locator usage, split DI files/options/validator helpers, added DI ordering/plugin-loading tests; `dotnet test src/__Libraries/__Tests/StellaOps.Cryptography.Tests/StellaOps.Cryptography.Tests.csproj -p:BuildInParallel=false -p:UseSharedCompilation=false` passed (326 tests). |