- Implemented the GostKeyValue class for handling public key parameters in ГОСТ Р 34.10 digital signatures. - Created the GostSignedXml class to manage XML signatures using ГОСТ 34.10, including methods for computing and checking signatures. - Developed the GostSignedXmlImpl class to encapsulate the signature computation logic and public key retrieval. - Added specific key value classes for ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2012/256, and ГОСТ Р 34.10-2012/512 to support different signature algorithms. - Ensured compatibility with existing XML signature standards while integrating ГОСТ cryptography.
106 lines
4.0 KiB
C#
106 lines
4.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using System.Runtime.Versioning;
|
|
using StellaOps.Cryptography;
|
|
|
|
namespace StellaOps.Cryptography.Plugin.CryptoPro;
|
|
|
|
[SupportedOSPlatform("windows")]
|
|
public sealed class CryptoProGostCryptoProvider : ICryptoProvider, ICryptoProviderDiagnostics
|
|
{
|
|
private readonly ILogger<CryptoProGostCryptoProvider>? logger;
|
|
private readonly IReadOnlyDictionary<string, CryptoProGostKeyEntry> entries;
|
|
|
|
public CryptoProGostCryptoProvider(
|
|
IOptions<CryptoProGostProviderOptions>? optionsAccessor = null,
|
|
ILogger<CryptoProGostCryptoProvider>? logger = null)
|
|
{
|
|
this.logger = logger;
|
|
var options = optionsAccessor?.Value ?? new CryptoProGostProviderOptions();
|
|
var map = new Dictionary<string, CryptoProGostKeyEntry>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var key in options.Keys)
|
|
{
|
|
var certificate = CryptoProCertificateResolver.Resolve(key);
|
|
var entry = new CryptoProGostKeyEntry(
|
|
key.KeyId,
|
|
key.Algorithm,
|
|
certificate,
|
|
key.ProviderName,
|
|
key.ContainerName,
|
|
key.UseMachineKeyStore,
|
|
key.SignatureFormat);
|
|
|
|
map[key.KeyId] = entry;
|
|
}
|
|
|
|
entries = map;
|
|
}
|
|
|
|
public string Name => "ru.cryptopro.csp";
|
|
|
|
public bool Supports(CryptoCapability capability, string algorithmId)
|
|
=> capability is CryptoCapability.Signing or CryptoCapability.Verification
|
|
&& (string.Equals(algorithmId, SignatureAlgorithms.GostR3410_2012_256, StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(algorithmId, SignatureAlgorithms.GostR3410_2012_512, StringComparison.OrdinalIgnoreCase));
|
|
|
|
public IPasswordHasher GetPasswordHasher(string algorithmId)
|
|
=> throw new NotSupportedException("CryptoPro provider does not expose password hashing.");
|
|
|
|
public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(keyReference);
|
|
var entry = ResolveKey(keyReference.KeyId);
|
|
if (!string.Equals(entry.AlgorithmId, algorithmId, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Signing key '{keyReference.KeyId}' is registered for algorithm '{entry.AlgorithmId}', not '{algorithmId}'.");
|
|
}
|
|
|
|
logger?.LogDebug("Using CryptoPro key {Key} ({Algorithm})", entry.KeyId, entry.AlgorithmId);
|
|
return new CryptoProGostSigner(entry);
|
|
}
|
|
|
|
public void UpsertSigningKey(CryptoSigningKey signingKey)
|
|
=> throw new NotSupportedException("CryptoPro keys are managed externally.");
|
|
|
|
public bool RemoveSigningKey(string keyId) => false;
|
|
|
|
public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
|
|
=> Array.Empty<CryptoSigningKey>();
|
|
|
|
public IEnumerable<CryptoProviderKeyDescriptor> DescribeKeys()
|
|
{
|
|
foreach (var entry in entries.Values)
|
|
{
|
|
yield return new CryptoProviderKeyDescriptor(
|
|
Name,
|
|
entry.KeyId,
|
|
entry.AlgorithmId,
|
|
new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["provider"] = entry.ProviderName,
|
|
["container"] = entry.ContainerName,
|
|
["thumbprint"] = entry.Certificate.Thumbprint,
|
|
["subject"] = entry.Certificate.Subject
|
|
});
|
|
}
|
|
}
|
|
|
|
private CryptoProGostKeyEntry ResolveKey(string? keyId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(keyId))
|
|
{
|
|
throw new ArgumentException("Crypto key reference must include KeyId.", nameof(keyId));
|
|
}
|
|
|
|
if (entries.TryGetValue(keyId, out var entry))
|
|
{
|
|
return entry;
|
|
}
|
|
|
|
throw new KeyNotFoundException($"CryptoPro key '{keyId}' is not registered.");
|
|
}
|
|
}
|