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