feat(api): Implement Console Export Client and Models
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
mock-dev-release / package-mock-release (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
mock-dev-release / package-mock-release (push) Has been cancelled
- Added ConsoleExportClient for managing export requests and responses. - Introduced ConsoleExportRequest and ConsoleExportResponse models. - Implemented methods for creating and retrieving exports with appropriate headers. feat(crypto): Add Software SM2/SM3 Cryptography Provider - Implemented SmSoftCryptoProvider for software-only SM2/SM3 cryptography. - Added support for signing and verification using SM2 algorithm. - Included hashing functionality with SM3 algorithm. - Configured options for loading keys from files and environment gate checks. test(crypto): Add unit tests for SmSoftCryptoProvider - Created comprehensive tests for signing, verifying, and hashing functionalities. - Ensured correct behavior for key management and error handling. feat(api): Enhance Console Export Models - Expanded ConsoleExport models to include detailed status and event types. - Added support for various export formats and notification options. test(time): Implement TimeAnchorPolicyService tests - Developed tests for TimeAnchorPolicyService to validate time anchors. - Covered scenarios for anchor validation, drift calculation, and policy enforcement.
This commit is contained in:
@@ -10,6 +10,7 @@ using StellaOps.Cryptography.Plugin.CryptoPro;
|
||||
#endif
|
||||
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
|
||||
using StellaOps.Cryptography.Plugin.OpenSslGost;
|
||||
using StellaOps.Cryptography.Plugin.SmSoft;
|
||||
|
||||
namespace StellaOps.Cryptography.DependencyInjection;
|
||||
|
||||
@@ -66,6 +67,7 @@ public static class CryptoServiceCollectionExtensions
|
||||
|
||||
services.TryAddSingleton<ICryptoHash, DefaultCryptoHash>();
|
||||
services.TryAddSingleton<ICryptoHmac, DefaultCryptoHmac>();
|
||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, SmSoftCryptoProvider>());
|
||||
|
||||
services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ProjectReference Include="..\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Cryptography.Plugin.Pkcs11Gost\StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Cryptography.Plugin.OpenSslGost\StellaOps.Cryptography.Plugin.OpenSslGost.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Org.BouncyCastle.Asn1.GM;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using StellaOps.Cryptography;
|
||||
|
||||
namespace StellaOps.Cryptography.Plugin.SmSoft;
|
||||
|
||||
/// <summary>
|
||||
/// Software-only SM2/SM3 provider (non-certified). Guarded by SM_SOFT_ALLOWED env by default.
|
||||
/// </summary>
|
||||
public sealed class SmSoftCryptoProvider : ICryptoProvider, ICryptoProviderDiagnostics
|
||||
{
|
||||
private const string EnvGate = "SM_SOFT_ALLOWED";
|
||||
private readonly ConcurrentDictionary<string, SmSoftKeyEntry> keys = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ILogger<SmSoftCryptoProvider> logger;
|
||||
private readonly SmSoftProviderOptions options;
|
||||
|
||||
public SmSoftCryptoProvider(
|
||||
IOptions<SmSoftProviderOptions>? optionsAccessor = null,
|
||||
ILogger<SmSoftCryptoProvider>? logger = null)
|
||||
{
|
||||
options = optionsAccessor?.Value ?? new SmSoftProviderOptions();
|
||||
this.logger = logger ?? NullLogger<SmSoftCryptoProvider>.Instance;
|
||||
|
||||
foreach (var key in options.Keys)
|
||||
{
|
||||
TryLoadKeyFromFile(key);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name => "cn.sm.soft";
|
||||
|
||||
public bool Supports(CryptoCapability capability, string algorithmId)
|
||||
{
|
||||
if (!GateEnabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return capability switch
|
||||
{
|
||||
CryptoCapability.Signing or CryptoCapability.Verification
|
||||
=> string.Equals(algorithmId, SignatureAlgorithms.Sm2, StringComparison.OrdinalIgnoreCase),
|
||||
CryptoCapability.ContentHashing
|
||||
=> string.Equals(algorithmId, HashAlgorithms.Sm3, StringComparison.OrdinalIgnoreCase),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public IPasswordHasher GetPasswordHasher(string algorithmId)
|
||||
=> throw new NotSupportedException("SM provider does not expose password hashing.");
|
||||
|
||||
public ICryptoHasher GetHasher(string algorithmId)
|
||||
{
|
||||
EnsureAllowed();
|
||||
if (!string.Equals(algorithmId, HashAlgorithms.Sm3, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Hash algorithm '{algorithmId}' is not supported by provider '{Name}'.");
|
||||
}
|
||||
|
||||
return new Sm3CryptoHasher();
|
||||
}
|
||||
|
||||
public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference)
|
||||
{
|
||||
EnsureAllowed();
|
||||
ArgumentNullException.ThrowIfNull(keyReference);
|
||||
|
||||
if (!string.Equals(algorithmId, SignatureAlgorithms.Sm2, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider '{Name}'.");
|
||||
}
|
||||
|
||||
if (!keys.TryGetValue(keyReference.KeyId, out var entry))
|
||||
{
|
||||
throw new KeyNotFoundException($"Signing key '{keyReference.KeyId}' is not registered with provider '{Name}'.");
|
||||
}
|
||||
|
||||
return new Sm2SoftSigner(entry);
|
||||
}
|
||||
|
||||
public void UpsertSigningKey(CryptoSigningKey signingKey)
|
||||
{
|
||||
EnsureAllowed();
|
||||
ArgumentNullException.ThrowIfNull(signingKey);
|
||||
|
||||
if (!string.Equals(signingKey.AlgorithmId, SignatureAlgorithms.Sm2, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Signing algorithm '{signingKey.AlgorithmId}' is not supported by provider '{Name}'.");
|
||||
}
|
||||
|
||||
// Accept raw key bytes (PKCS#8 DER) or ECParameters are not SM2-compatible in BCL.
|
||||
if (signingKey.PrivateKey.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException("SM2 provider requires raw private key bytes (PKCS#8 DER).");
|
||||
}
|
||||
|
||||
var keyPair = LoadKeyPair(signingKey.PrivateKey.ToArray());
|
||||
var entry = new SmSoftKeyEntry(signingKey.Reference.KeyId, keyPair);
|
||||
keys.AddOrUpdate(signingKey.Reference.KeyId, entry, (_, _) => entry);
|
||||
}
|
||||
|
||||
public bool RemoveSigningKey(string keyId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keyId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return keys.TryRemove(keyId, out _);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
|
||||
=> Array.Empty<CryptoSigningKey>(); // software keys are managed externally or via raw bytes; we don't expose private material.
|
||||
|
||||
public IEnumerable<CryptoProviderKeyDescriptor> DescribeKeys()
|
||||
{
|
||||
foreach (var entry in keys.Values)
|
||||
{
|
||||
yield return new CryptoProviderKeyDescriptor(
|
||||
Name,
|
||||
entry.KeyId,
|
||||
SignatureAlgorithms.Sm2,
|
||||
new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["provider"] = Name,
|
||||
["label"] = entry.KeyId,
|
||||
["software"] = "true",
|
||||
["certified"] = "false"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool GateEnabled()
|
||||
{
|
||||
if (!options.RequireEnvironmentGate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(Environment.GetEnvironmentVariable(EnvGate), "1", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void EnsureAllowed()
|
||||
{
|
||||
if (!GateEnabled())
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Provider '{Name}' is disabled. Set {EnvGate}=1 (or disable RequireEnvironmentGate) to enable software SM2/SM3.");
|
||||
}
|
||||
}
|
||||
|
||||
private void TryLoadKeyFromFile(SmSoftKeyOptions key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key.KeyId) || string.IsNullOrWhiteSpace(key.PrivateKeyPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var bytes = File.ReadAllBytes(key.PrivateKeyPath);
|
||||
var keyPair = LoadKeyPair(bytes);
|
||||
var entry = new SmSoftKeyEntry(key.KeyId, keyPair);
|
||||
keys.TryAdd(key.KeyId, entry);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to load SM2 key {KeyId} from {Path}", key.KeyId, key.PrivateKeyPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static AsymmetricCipherKeyPair LoadKeyPair(byte[] data)
|
||||
{
|
||||
// Try PEM first, then DER PKCS#8
|
||||
try
|
||||
{
|
||||
using var reader = new StreamReader(new MemoryStream(data));
|
||||
var pem = new PemReader(reader).ReadObject();
|
||||
if (pem is AsymmetricCipherKeyPair pair)
|
||||
{
|
||||
return pair;
|
||||
}
|
||||
|
||||
if (pem is ECPrivateKeyParameters priv)
|
||||
{
|
||||
var q = priv.Parameters.G.Multiply(priv.D);
|
||||
var pub = new ECPublicKeyParameters(q, priv.Parameters);
|
||||
return new AsymmetricCipherKeyPair(pub, priv);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall through to DER parsing
|
||||
}
|
||||
|
||||
var key = PrivateKeyFactory.CreateKey(data);
|
||||
if (key is ECPrivateKeyParameters ecPriv)
|
||||
{
|
||||
var q = ecPriv.Parameters.G.Multiply(ecPriv.D);
|
||||
var pub = new ECPublicKeyParameters(q, ecPriv.Parameters);
|
||||
return new AsymmetricCipherKeyPair(pub, ecPriv);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unsupported SM2 key format. Expect PEM or PKCS#8 DER.");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record SmSoftKeyEntry(string KeyId, AsymmetricCipherKeyPair KeyPair);
|
||||
|
||||
internal sealed class Sm2SoftSigner : ICryptoSigner
|
||||
{
|
||||
private static readonly byte[] DefaultUserId = System.Text.Encoding.ASCII.GetBytes("1234567812345678");
|
||||
private readonly SmSoftKeyEntry entry;
|
||||
|
||||
public Sm2SoftSigner(SmSoftKeyEntry entry)
|
||||
{
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public string KeyId => entry.KeyId;
|
||||
|
||||
public string AlgorithmId => SignatureAlgorithms.Sm2;
|
||||
|
||||
public async ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var signer = new SM2Signer();
|
||||
signer.Init(true, new ParametersWithID(entry.KeyPair.Private, DefaultUserId));
|
||||
signer.BlockUpdate(data.Span);
|
||||
return await Task.FromResult(signer.GenerateSignature());
|
||||
}
|
||||
|
||||
public async ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var verifier = new SM2Signer();
|
||||
verifier.Init(false, new ParametersWithID(entry.KeyPair.Public, DefaultUserId));
|
||||
verifier.BlockUpdate(data.Span);
|
||||
var result = verifier.VerifySignature(signature.Span.ToArray());
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public JsonWebKey ExportPublicJsonWebKey()
|
||||
{
|
||||
var pub = (ECPublicKeyParameters)entry.KeyPair.Public;
|
||||
var q = pub.Q.Normalize();
|
||||
var x = q.XCoord.GetEncoded();
|
||||
var y = q.YCoord.GetEncoded();
|
||||
|
||||
return new JsonWebKey
|
||||
{
|
||||
Kid = KeyId,
|
||||
Kty = "EC",
|
||||
Crv = "SM2",
|
||||
Alg = SignatureAlgorithms.Sm2,
|
||||
Use = "sig",
|
||||
X = Base64UrlEncoder.Encode(x),
|
||||
Y = Base64UrlEncoder.Encode(y)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Sm3CryptoHasher : ICryptoHasher
|
||||
{
|
||||
public string AlgorithmId => HashAlgorithms.Sm3;
|
||||
|
||||
public byte[] ComputeHash(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var digest = new Org.BouncyCastle.Crypto.Digests.SM3Digest();
|
||||
digest.BlockUpdate(data);
|
||||
var output = new byte[digest.GetDigestSize()];
|
||||
digest.DoFinal(output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
public string ComputeHashHex(ReadOnlySpan<byte> data)
|
||||
=> Convert.ToHexString(ComputeHash(data)).ToLowerInvariant();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Cryptography.Plugin.SmSoft;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the software-only SM provider.
|
||||
/// </summary>
|
||||
public sealed class SmSoftProviderOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional key entries loaded from PEM/DER to seed the provider.
|
||||
/// </summary>
|
||||
public IList<SmSoftKeyOptions> Keys { get; } = new List<SmSoftKeyOptions>();
|
||||
|
||||
/// <summary>
|
||||
/// Require an explicit opt-in (default: true). If false, provider is active without env gate.
|
||||
/// </summary>
|
||||
public bool RequireEnvironmentGate { get; set; } = true;
|
||||
}
|
||||
|
||||
public sealed class SmSoftKeyOptions
|
||||
{
|
||||
public string KeyId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Private key file path (PEM or DER) for SM2.</summary>
|
||||
public string PrivateKeyPath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Signature algorithm, default SM2.</summary>
|
||||
public string Algorithm { get; set; } = StellaOps.Cryptography.SignatureAlgorithms.Sm2;
|
||||
|
||||
/// <summary>Optional label or metadata.</summary>
|
||||
public string? Label { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -12,5 +12,5 @@ public static class SignatureAlgorithms
|
||||
public const string EdDsa = "EdDSA";
|
||||
public const string GostR3410_2012_256 = "GOST12-256";
|
||||
public const string GostR3410_2012_512 = "GOST12-512";
|
||||
public const string Sm2 = "SM2";
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Org.BouncyCastle.Asn1.GM;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Asn1.Pkcs;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Plugin.SmSoft;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cryptography.Tests;
|
||||
|
||||
public class SmSoftCryptoProviderTests : IDisposable
|
||||
{
|
||||
private readonly string? _originalGate;
|
||||
|
||||
public SmSoftCryptoProviderTests()
|
||||
{
|
||||
_originalGate = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");
|
||||
Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", "1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignAndVerify_Sm2_Works()
|
||||
{
|
||||
var provider = new SmSoftCryptoProvider();
|
||||
var key = GenerateSm2Key();
|
||||
|
||||
provider.UpsertSigningKey(key);
|
||||
|
||||
var signer = provider.GetSigner(SignatureAlgorithms.Sm2, key.Reference);
|
||||
var payload = Encoding.UTF8.GetBytes("sm2-payload");
|
||||
|
||||
var signature = await signer.SignAsync(payload);
|
||||
Assert.True(await signer.VerifyAsync(payload, signature));
|
||||
|
||||
var jwk = signer.ExportPublicJsonWebKey();
|
||||
Assert.Equal(SignatureAlgorithms.Sm2, jwk.Alg);
|
||||
Assert.Equal("SM2", jwk.Crv);
|
||||
Assert.Equal(key.Reference.KeyId, jwk.Kid);
|
||||
Assert.False(string.IsNullOrEmpty(jwk.X));
|
||||
Assert.False(string.IsNullOrEmpty(jwk.Y));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Hash_Sm3_Works()
|
||||
{
|
||||
var provider = new SmSoftCryptoProvider();
|
||||
var hasher = provider.GetHasher(HashAlgorithms.Sm3);
|
||||
|
||||
var digest = hasher.ComputeHashHex(Encoding.UTF8.GetBytes("abc"));
|
||||
// Known SM3("abc") = 66c7f0f462eeedd9d1f2d46bdc10e4e2 4167c4875cf2f7a2 297da02b8f4ba8e0
|
||||
Assert.Equal("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", digest);
|
||||
}
|
||||
|
||||
private static CryptoSigningKey GenerateSm2Key()
|
||||
{
|
||||
var generator = new ECKeyPairGenerator("EC");
|
||||
var curve = GMNamedCurves.GetByName("SM2P256V1");
|
||||
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
|
||||
generator.Init(new ECKeyGenerationParameters(domain, new SecureRandom(new CryptoApiRandomGenerator())));
|
||||
var pair = generator.GenerateKeyPair();
|
||||
var privateDer = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private).ToAsn1Object().GetDerEncoded();
|
||||
|
||||
var keyRef = new CryptoKeyReference("sm-soft-test");
|
||||
return new CryptoSigningKey(keyRef, SignatureAlgorithms.Sm2, privateDer, DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", _originalGate);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
<ProjectReference Include="../../StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
|
||||
<ProjectReference Include="../../StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj" />
|
||||
<ProjectReference Include="../../StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj" />
|
||||
<ProjectReference Include="../../StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(StellaOpsEnableCryptoPro)' == 'true'">
|
||||
<ProjectReference Include="../../StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj" />
|
||||
|
||||
Reference in New Issue
Block a user