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,34 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
namespace StellaOps.Cryptography.Plugin.EIDAS.Configuration;
/// <summary>
/// eIDAS key configuration.
/// </summary>
public class EidasKeyConfig
{
/// <summary>
/// Unique key identifier.
/// </summary>
public required string KeyId { get; set; }
/// <summary>
/// Key source: "tsp" (remote) or "local" (PKCS#12).
/// </summary>
public required string Source { get; set; }
/// <summary>
/// Certificate in PEM format (optional for validation).
/// </summary>
public string? Certificate { get; set; }
/// <summary>
/// Certificate subject DN.
/// </summary>
public string? SubjectDn { get; set; }
/// <summary>
/// Certificate serial number.
/// </summary>
public string? SerialNumber { get; set; }
}

View File

@@ -1,6 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using StellaOps.Cryptography.Plugin.EIDAS.Models;
namespace StellaOps.Cryptography.Plugin.EIDAS.Configuration;
@@ -60,113 +59,3 @@ public class EidasOptions
/// </summary>
public List<EidasKeyConfig> Keys { get; set; } = new();
}
/// <summary>
/// Trust Service Provider configuration for remote QES signing.
/// </summary>
public class TspOptions
{
/// <summary>
/// TSP API endpoint URL.
/// </summary>
public required string Endpoint { get; set; }
/// <summary>
/// TSP API key for authentication.
/// </summary>
public required string ApiKey { get; set; }
/// <summary>
/// TSP certificate for mutual TLS (optional).
/// </summary>
public string? Certificate { get; set; }
/// <summary>
/// Request timeout in seconds.
/// </summary>
public int TimeoutSeconds { get; set; } = 30;
}
/// <summary>
/// Local signing configuration (PKCS#12 keystore).
/// </summary>
public class LocalSigningOptions
{
/// <summary>
/// Keystore type (PKCS12, PEM).
/// </summary>
public string Type { get; set; } = "PKCS12";
/// <summary>
/// Path to keystore file.
/// </summary>
public required string Path { get; set; }
/// <summary>
/// Keystore password.
/// </summary>
public required string Password { get; set; }
/// <summary>
/// Path to certificate chain file (PEM format).
/// </summary>
public string? CertificateChainPath { get; set; }
}
/// <summary>
/// EU Trusted List configuration.
/// </summary>
public class TrustedListOptions
{
/// <summary>
/// EU Trusted List (EUTL) URL.
/// Default: https://ec.europa.eu/tools/lotl/eu-lotl.xml
/// </summary>
public string Url { get; set; } = "https://ec.europa.eu/tools/lotl/eu-lotl.xml";
/// <summary>
/// Local cache directory for trusted list.
/// </summary>
public string CachePath { get; set; } = "./crypto/eutl-cache";
/// <summary>
/// Refresh interval in hours.
/// </summary>
public int RefreshIntervalHours { get; set; } = 24;
/// <summary>
/// Enable strict validation (fail on any validation error).
/// </summary>
public bool StrictValidation { get; set; } = true;
}
/// <summary>
/// eIDAS key configuration.
/// </summary>
public class EidasKeyConfig
{
/// <summary>
/// Unique key identifier.
/// </summary>
public required string KeyId { get; set; }
/// <summary>
/// Key source: "tsp" (remote) or "local" (PKCS#12).
/// </summary>
public required string Source { get; set; }
/// <summary>
/// Certificate in PEM format (optional for validation).
/// </summary>
public string? Certificate { get; set; }
/// <summary>
/// Certificate subject DN.
/// </summary>
public string? SubjectDn { get; set; }
/// <summary>
/// Certificate serial number.
/// </summary>
public string? SerialNumber { get; set; }
}

View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
namespace StellaOps.Cryptography.Plugin.EIDAS.Configuration;
/// <summary>
/// Local signing configuration (PKCS#12 keystore).
/// </summary>
public class LocalSigningOptions
{
/// <summary>
/// Keystore type (PKCS12, PEM).
/// </summary>
public string Type { get; set; } = "PKCS12";
/// <summary>
/// Path to keystore file.
/// </summary>
public required string Path { get; set; }
/// <summary>
/// Keystore password.
/// </summary>
public required string Password { get; set; }
/// <summary>
/// Path to certificate chain file (PEM format).
/// </summary>
public string? CertificateChainPath { get; set; }
}

View File

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
namespace StellaOps.Cryptography.Plugin.EIDAS.Configuration;
/// <summary>
/// EU Trusted List configuration.
/// </summary>
public class TrustedListOptions
{
/// <summary>
/// EU Trusted List (EUTL) URL.
/// Default: https://ec.europa.eu/tools/lotl/eu-lotl.xml
/// </summary>
public string Url { get; set; } = "https://ec.europa.eu/tools/lotl/eu-lotl.xml";
/// <summary>
/// Local cache directory for trusted list.
/// </summary>
public string CachePath { get; set; } = "./crypto/eutl-cache";
/// <summary>
/// Refresh interval in hours.
/// </summary>
public int RefreshIntervalHours { get; set; } = 24;
/// <summary>
/// Enable strict validation (fail on any validation error).
/// </summary>
public bool StrictValidation { get; set; } = true;
}

View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
namespace StellaOps.Cryptography.Plugin.EIDAS.Configuration;
/// <summary>
/// Trust Service Provider configuration for remote QES signing.
/// </summary>
public class TspOptions
{
/// <summary>
/// TSP API endpoint URL.
/// </summary>
public required string Endpoint { get; set; }
/// <summary>
/// TSP API key for authentication.
/// </summary>
public required string ApiKey { get; set; }
/// <summary>
/// TSP certificate for mutual TLS (optional).
/// </summary>
public string? Certificate { get; set; }
/// <summary>
/// Request timeout in seconds.
/// </summary>
public int TimeoutSeconds { get; set; } = 30;
}

View File

@@ -1,6 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
@@ -82,6 +81,7 @@ public class EidasCryptoProvider : ICryptoProvider
{
_logger.LogInformation("eIDAS signing key removed: keyId={KeyId}", keyId);
}
return removed;
}
@@ -90,112 +90,3 @@ public class EidasCryptoProvider : ICryptoProvider
return _signingKeys.Values.ToList().AsReadOnly();
}
}
/// <summary>
/// eIDAS signer implementation that routes to TSP or local provider.
/// </summary>
internal class EidasSigner : ICryptoSigner
{
private readonly ILogger _logger;
private readonly EidasOptions _options;
private readonly TrustServiceProviderClient _tspClient;
private readonly LocalEidasProvider _localProvider;
private readonly string _algorithmId;
private readonly CryptoKeyReference _keyReference;
public EidasSigner(
ILogger logger,
EidasOptions options,
TrustServiceProviderClient tspClient,
LocalEidasProvider localProvider,
string algorithmId,
CryptoKeyReference keyReference)
{
_logger = logger;
_options = options;
_tspClient = tspClient;
_localProvider = localProvider;
_algorithmId = algorithmId;
_keyReference = keyReference;
}
public string KeyId => _keyReference.KeyId;
public string AlgorithmId => _algorithmId;
public async ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
{
_logger.LogDebug("eIDAS signing request: keyId={KeyId}, algorithm={Algorithm}",
_keyReference.KeyId, _algorithmId);
// Resolve key configuration
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig == null)
{
throw new KeyNotFoundException($"eIDAS key '{_keyReference.KeyId}' not configured");
}
// Route to appropriate signer based on key source
byte[] signature = keyConfig.Source.ToLowerInvariant() switch
{
"tsp" => await _tspClient.RemoteSignAsync(data.ToArray(), _algorithmId, keyConfig, cancellationToken),
"local" => await _localProvider.LocalSignAsync(data.ToArray(), _algorithmId, keyConfig, cancellationToken),
_ => throw new InvalidOperationException($"Unsupported eIDAS key source: {keyConfig.Source}")
};
_logger.LogInformation("eIDAS signature created: keyId={KeyId}, signatureLength={Length}, level={Level}",
_keyReference.KeyId, signature.Length, _options.SignatureLevel);
return signature;
}
public async ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken cancellationToken = default)
{
_logger.LogDebug("eIDAS verification request: keyId={KeyId}, algorithm={Algorithm}",
_keyReference.KeyId, _algorithmId);
// Resolve key configuration
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig == null)
{
throw new KeyNotFoundException($"eIDAS key '{_keyReference.KeyId}' not configured");
}
// Route to appropriate verifier
bool isValid = keyConfig.Source.ToLowerInvariant() switch
{
"tsp" => await _tspClient.RemoteVerifyAsync(data.ToArray(), signature.ToArray(), _algorithmId, keyConfig, cancellationToken),
"local" => await _localProvider.LocalVerifyAsync(data.ToArray(), signature.ToArray(), _algorithmId, keyConfig, cancellationToken),
_ => throw new InvalidOperationException($"Unsupported eIDAS key source: {keyConfig.Source}")
};
_logger.LogInformation("eIDAS verification result: keyId={KeyId}, valid={Valid}",
_keyReference.KeyId, isValid);
return isValid;
}
public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey()
{
// For eIDAS, public key export requires certificate parsing
// Stub implementation - in production, extract from certificate
_logger.LogWarning("eIDAS ExportPublicJsonWebKey is not fully implemented - returning stub JWK");
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig?.Certificate != null)
{
// Production: Parse certificate and extract public key
// var cert = X509Certificate2.CreateFromPem(keyConfig.Certificate);
// var ecdsa = cert.GetECDsaPublicKey();
// return JsonWebKeyConverter.ConvertFromECDsaSecurityKey(new ECDsaSecurityKey(ecdsa));
}
return new Microsoft.IdentityModel.Tokens.JsonWebKey
{
Kty = "EC",
Crv = "P-256",
Use = "sig",
Kid = _keyReference.KeyId,
Alg = _algorithmId
};
}
}

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
namespace StellaOps.Cryptography.Plugin.EIDAS;
internal partial class EidasSigner
{
public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey()
{
// For eIDAS, public key export requires certificate parsing
// Stub implementation - in production, extract from certificate
_logger.LogWarning("eIDAS ExportPublicJsonWebKey is not fully implemented - returning stub JWK");
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig?.Certificate != null)
{
// Production: Parse certificate and extract public key
// var cert = X509Certificate2.CreateFromPem(keyConfig.Certificate);
// var ecdsa = cert.GetECDsaPublicKey();
// return JsonWebKeyConverter.ConvertFromECDsaSecurityKey(new ECDsaSecurityKey(ecdsa));
}
return new Microsoft.IdentityModel.Tokens.JsonWebKey
{
Kty = "EC",
Crv = "P-256",
Use = "sig",
Kid = _keyReference.KeyId,
Alg = _algorithmId
};
}
}

View File

@@ -0,0 +1,99 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
namespace StellaOps.Cryptography.Plugin.EIDAS;
/// <summary>
/// eIDAS signer implementation that routes to TSP or local provider.
/// </summary>
internal partial class EidasSigner : ICryptoSigner
{
private readonly ILogger _logger;
private readonly EidasOptions _options;
private readonly TrustServiceProviderClient _tspClient;
private readonly LocalEidasProvider _localProvider;
private readonly string _algorithmId;
private readonly CryptoKeyReference _keyReference;
public EidasSigner(
ILogger logger,
EidasOptions options,
TrustServiceProviderClient tspClient,
LocalEidasProvider localProvider,
string algorithmId,
CryptoKeyReference keyReference)
{
_logger = logger;
_options = options;
_tspClient = tspClient;
_localProvider = localProvider;
_algorithmId = algorithmId;
_keyReference = keyReference;
}
public string KeyId => _keyReference.KeyId;
public string AlgorithmId => _algorithmId;
public async ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
{
_logger.LogDebug("eIDAS signing request: keyId={KeyId}, algorithm={Algorithm}",
_keyReference.KeyId, _algorithmId);
// Resolve key configuration
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig == null)
{
throw new KeyNotFoundException($"eIDAS key '{_keyReference.KeyId}' not configured");
}
// Route to appropriate signer based on key source
byte[] signature = keyConfig.Source.ToLowerInvariant() switch
{
"tsp" => await _tspClient.RemoteSignAsync(data.ToArray(), _algorithmId, keyConfig, cancellationToken)
.ConfigureAwait(false),
"local" => await _localProvider.LocalSignAsync(data.ToArray(), _algorithmId, keyConfig, cancellationToken)
.ConfigureAwait(false),
_ => throw new InvalidOperationException($"Unsupported eIDAS key source: {keyConfig.Source}")
};
_logger.LogInformation("eIDAS signature created: keyId={KeyId}, signatureLength={Length}, level={Level}",
_keyReference.KeyId, signature.Length, _options.SignatureLevel);
return signature;
}
public async ValueTask<bool> VerifyAsync(
ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> signature,
CancellationToken cancellationToken = default)
{
_logger.LogDebug("eIDAS verification request: keyId={KeyId}, algorithm={Algorithm}",
_keyReference.KeyId, _algorithmId);
// Resolve key configuration
var keyConfig = _options.Keys.FirstOrDefault(k => k.KeyId == _keyReference.KeyId);
if (keyConfig == null)
{
throw new KeyNotFoundException($"eIDAS key '{_keyReference.KeyId}' not configured");
}
// Route to appropriate verifier
bool isValid = keyConfig.Source.ToLowerInvariant() switch
{
"tsp" => await _tspClient.RemoteVerifyAsync(data.ToArray(), signature.ToArray(), _algorithmId, keyConfig, cancellationToken)
.ConfigureAwait(false),
"local" => await _localProvider.LocalVerifyAsync(data.ToArray(), signature.ToArray(), _algorithmId, keyConfig, cancellationToken)
.ConfigureAwait(false),
_ => throw new InvalidOperationException($"Unsupported eIDAS key source: {keyConfig.Source}")
};
_logger.LogInformation("eIDAS verification result: keyId={KeyId}, valid={Valid}",
_keyReference.KeyId, isValid);
return isValid;
}
}

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using System.Security.Cryptography;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
namespace StellaOps.Cryptography.Plugin.EIDAS;
public partial class LocalEidasProvider
{
/// <summary>
/// Local signing with PKCS#12 certificate (stub implementation).
/// </summary>
public async Task<byte[]> LocalSignAsync(
byte[] data,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("Local eIDAS signing: keyId={KeyId}, algorithm={Algorithm}, dataLength={Length}",
keyConfig.KeyId, algorithmId, data.Length);
if (_options == null)
{
throw new InvalidOperationException("Local signing options not configured");
}
// Load certificate from PKCS#12 keystore (cached)
_certificate ??= LoadCertificate(_options);
// Stub implementation - in production, use actual certificate signing
_logger.LogWarning("Using stub local signing - replace with actual PKCS#12 signing in production");
// Compute hash
var hash = algorithmId.Contains("SHA256") ? SHA256.HashData(data) : SHA512.HashData(data);
// Stub: Create mock signature
var stubSignature = new byte[64]; // ECDSA-P256 signature
RandomNumberGenerator.Fill(stubSignature);
_logger.LogInformation("Local eIDAS signature created (stub): keyId={KeyId}, signatureLength={Length}",
keyConfig.KeyId, stubSignature.Length);
await Task.CompletedTask.ConfigureAwait(false);
return stubSignature;
// Production implementation:
// using var rsa = _certificate.GetRSAPrivateKey();
// using var ecdsa = _certificate.GetECDsaPrivateKey();
//
// return algorithmId switch
// {
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.SignData(data, HashAlgorithmName.SHA256),
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local signing")
// };
}
}

View File

@@ -0,0 +1,51 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
namespace StellaOps.Cryptography.Plugin.EIDAS;
public partial class LocalEidasProvider
{
/// <summary>
/// Local verification with PKCS#12 certificate (stub implementation).
/// </summary>
public async Task<bool> LocalVerifyAsync(
byte[] data,
byte[] signature,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("Local eIDAS verification: keyId={KeyId}, algorithm={Algorithm}",
keyConfig.KeyId, algorithmId);
if (_options == null)
{
throw new InvalidOperationException("Local signing options not configured");
}
// Load certificate from PKCS#12 keystore
_certificate ??= LoadCertificate(_options);
// Stub: Always return true
_logger.LogWarning("Using stub local verification - replace with actual PKCS#12 verification in production");
await Task.Delay(10, cancellationToken).ConfigureAwait(false); // Simulate crypto operation
_logger.LogInformation("Local eIDAS verification complete (stub): keyId={KeyId}, valid=true",
keyConfig.KeyId);
return true;
// Production implementation:
// using var rsa = _certificate.GetRSAPublicKey();
// using var ecdsa = _certificate.GetECDsaPublicKey();
//
// return algorithmId switch
// {
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256),
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local verification")
// };
}
}

View File

@@ -1,10 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace StellaOps.Cryptography.Plugin.EIDAS;
@@ -13,7 +11,7 @@ namespace StellaOps.Cryptography.Plugin.EIDAS;
/// Local eIDAS signing provider using PKCS#12 keystores.
/// Suitable for development and AdES-level signatures.
/// </summary>
public class LocalEidasProvider
public partial class LocalEidasProvider
{
private readonly ILogger<LocalEidasProvider> _logger;
private readonly LocalSigningOptions? _options;
@@ -27,96 +25,6 @@ public class LocalEidasProvider
_options = options.Value.Local;
}
/// <summary>
/// Local signing with PKCS#12 certificate (stub implementation).
/// </summary>
public async Task<byte[]> LocalSignAsync(
byte[] data,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("Local eIDAS signing: keyId={KeyId}, algorithm={Algorithm}, dataLength={Length}",
keyConfig.KeyId, algorithmId, data.Length);
if (_options == null)
{
throw new InvalidOperationException("Local signing options not configured");
}
// Load certificate from PKCS#12 keystore (cached)
_certificate ??= LoadCertificate(_options);
// Stub implementation - in production, use actual certificate signing
_logger.LogWarning("Using stub local signing - replace with actual PKCS#12 signing in production");
// Compute hash
var hash = algorithmId.Contains("SHA256") ? SHA256.HashData(data) : SHA512.HashData(data);
// Stub: Create mock signature
var stubSignature = new byte[64]; // ECDSA-P256 signature
RandomNumberGenerator.Fill(stubSignature);
_logger.LogInformation("Local eIDAS signature created (stub): keyId={KeyId}, signatureLength={Length}",
keyConfig.KeyId, stubSignature.Length);
await Task.CompletedTask; // For async signature
return stubSignature;
// Production implementation:
// using var rsa = _certificate.GetRSAPrivateKey();
// using var ecdsa = _certificate.GetECDsaPrivateKey();
//
// return algorithmId switch
// {
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.SignData(data, HashAlgorithmName.SHA256),
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local signing")
// };
}
/// <summary>
/// Local verification with PKCS#12 certificate (stub implementation).
/// </summary>
public async Task<bool> LocalVerifyAsync(
byte[] data,
byte[] signature,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("Local eIDAS verification: keyId={KeyId}, algorithm={Algorithm}",
keyConfig.KeyId, algorithmId);
if (_options == null)
{
throw new InvalidOperationException("Local signing options not configured");
}
// Load certificate from PKCS#12 keystore
_certificate ??= LoadCertificate(_options);
// Stub: Always return true
_logger.LogWarning("Using stub local verification - replace with actual PKCS#12 verification in production");
await Task.Delay(10, cancellationToken); // Simulate crypto operation
_logger.LogInformation("Local eIDAS verification complete (stub): keyId={KeyId}, valid=true",
keyConfig.KeyId);
return true;
// Production implementation:
// using var rsa = _certificate.GetRSAPublicKey();
// using var ecdsa = _certificate.GetECDsaPublicKey();
//
// return algorithmId switch
// {
// "RSA-PSS-2048" or "RSA-PSS-4096" => rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
// "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256),
// _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported for local verification")
// };
}
private X509Certificate2 LoadCertificate(LocalSigningOptions options)
{
_logger.LogDebug("Loading eIDAS certificate from keystore: path={Path}, type={Type}",
@@ -141,7 +49,8 @@ public class LocalEidasProvider
return cert;
}
else if (options.Type.Equals("PEM", StringComparison.OrdinalIgnoreCase))
if (options.Type.Equals("PEM", StringComparison.OrdinalIgnoreCase))
{
// Load PEM certificate (requires separate key file)
var certPem = File.ReadAllText(options.Path);
@@ -152,10 +61,8 @@ public class LocalEidasProvider
return cert;
}
else
{
throw new NotSupportedException($"Keystore type '{options.Type}' not supported");
}
throw new NotSupportedException($"Keystore type '{options.Type}' not supported");
}
catch (Exception ex)
{

View File

@@ -10,3 +10,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0057-A | TODO | Revalidated 2026-01-08 (open findings). |
| TASK-033-004 | DONE | Fixed keystore/TSP test config; EIDAS tests pass (SPRINT_20260120_033). |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| REMED-05 | DONE | Split provider/options/client into <=100-line partials, added ConfigureAwait(false) in library awaits; `dotnet test src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj -p:BuildInParallel=false -p:UseSharedCompilation=false` passed (25 tests). |

View File

@@ -0,0 +1,64 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using System.Security.Cryptography;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
namespace StellaOps.Cryptography.Plugin.EIDAS;
public partial class TrustServiceProviderClient
{
/// <summary>
/// Remote signing via TSP (stub implementation).
/// </summary>
public async Task<byte[]> RemoteSignAsync(
byte[] data,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("TSP remote signing request: keyId={KeyId}, algorithm={Algorithm}, dataLength={Length}",
keyConfig.KeyId, algorithmId, data.Length);
// Stub implementation - in production, this would call actual TSP API
// Example TSP request format (vendor-specific):
// POST /api/v1/sign
// {
// "keyId": "...",
// "algorithm": "ECDSA-P256",
// "digestAlgorithm": "SHA256",
// "dataHash": "base64-encoded-hash",
// "signatureLevel": "QES"
// }
_logger.LogWarning("Using stub TSP implementation - replace with actual TSP API call in production");
// Compute hash for signing
var hash = algorithmId.Contains("SHA256") ? SHA256.HashData(data) : SHA512.HashData(data);
// Stub: Return mock signature
var stubSignature = new byte[64]; // ECDSA-P256 signature is 64 bytes
RandomNumberGenerator.Fill(stubSignature);
_logger.LogInformation("TSP remote signature created (stub): keyId={KeyId}, signatureLength={Length}",
keyConfig.KeyId, stubSignature.Length);
return stubSignature;
// Production implementation would be:
// var request = new
// {
// keyId = keyConfig.KeyId,
// algorithm = algorithmId,
// digestAlgorithm = "SHA256",
// dataHash = Convert.ToBase64String(hash),
// signatureLevel = "QES"
// };
//
// var response = await _httpClient.PostAsJsonAsync("/api/v1/sign", request, cancellationToken);
// response.EnsureSuccessStatusCode();
//
// var result = await response.Content.ReadFromJsonAsync<TspSignResponse>(cancellationToken);
// return Convert.FromBase64String(result.Signature);
}
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
namespace StellaOps.Cryptography.Plugin.EIDAS;
public partial class TrustServiceProviderClient
{
/// <summary>
/// Remote verification via TSP (stub implementation).
/// </summary>
public async Task<bool> RemoteVerifyAsync(
byte[] data,
byte[] signature,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("TSP remote verification request: keyId={KeyId}, algorithm={Algorithm}",
keyConfig.KeyId, algorithmId);
_logger.LogWarning("Using stub TSP verification - replace with actual TSP API call in production");
// Stub: Always return true
await Task.Delay(50, cancellationToken).ConfigureAwait(false); // Simulate network latency
_logger.LogInformation("TSP remote verification complete: keyId={KeyId}, valid=true",
keyConfig.KeyId);
return true;
// Production implementation would be:
// var hash = SHA256.HashData(data);
// var request = new
// {
// keyId = keyConfig.KeyId,
// algorithm = algorithmId,
// dataHash = Convert.ToBase64String(hash),
// signature = Convert.ToBase64String(signature)
// };
//
// var response = await _httpClient.PostAsJsonAsync("/api/v1/verify", request, cancellationToken);
// response.EnsureSuccessStatusCode();
//
// var result = await response.Content.ReadFromJsonAsync<TspVerifyResponse>(cancellationToken);
// return result.Valid;
}
}

View File

@@ -1,12 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography.Plugin.EIDAS.Configuration;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text.Json;
namespace StellaOps.Cryptography.Plugin.EIDAS;
@@ -14,7 +10,7 @@ namespace StellaOps.Cryptography.Plugin.EIDAS;
/// Client for Trust Service Provider (TSP) remote signing API.
/// Implements QES (Qualified Electronic Signature) with remote QSCD.
/// </summary>
public class TrustServiceProviderClient
public partial class TrustServiceProviderClient
{
private readonly ILogger<TrustServiceProviderClient> _logger;
private readonly HttpClient _httpClient;
@@ -34,102 +30,4 @@ public class TrustServiceProviderClient
_httpClient.Timeout = TimeSpan.FromSeconds(_options.TimeoutSeconds);
_httpClient.DefaultRequestHeaders.Add("X-API-Key", _options.ApiKey);
}
/// <summary>
/// Remote signing via TSP (stub implementation).
/// </summary>
public async Task<byte[]> RemoteSignAsync(
byte[] data,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("TSP remote signing request: keyId={KeyId}, algorithm={Algorithm}, dataLength={Length}",
keyConfig.KeyId, algorithmId, data.Length);
// Stub implementation - in production, this would call actual TSP API
// Example TSP request format (vendor-specific):
// POST /api/v1/sign
// {
// "keyId": "...",
// "algorithm": "ECDSA-P256",
// "digestAlgorithm": "SHA256",
// "dataHash": "base64-encoded-hash",
// "signatureLevel": "QES"
// }
_logger.LogWarning("Using stub TSP implementation - replace with actual TSP API call in production");
// Compute hash for signing
var hash = algorithmId.Contains("SHA256") ? SHA256.HashData(data) : SHA512.HashData(data);
// Stub: Return mock signature
var stubSignature = new byte[64]; // ECDSA-P256 signature is 64 bytes
RandomNumberGenerator.Fill(stubSignature);
_logger.LogInformation("TSP remote signature created (stub): keyId={KeyId}, signatureLength={Length}",
keyConfig.KeyId, stubSignature.Length);
return stubSignature;
// Production implementation would be:
// var request = new
// {
// keyId = keyConfig.KeyId,
// algorithm = algorithmId,
// digestAlgorithm = "SHA256",
// dataHash = Convert.ToBase64String(hash),
// signatureLevel = "QES"
// };
//
// var response = await _httpClient.PostAsJsonAsync("/api/v1/sign", request, cancellationToken);
// response.EnsureSuccessStatusCode();
//
// var result = await response.Content.ReadFromJsonAsync<TspSignResponse>(cancellationToken);
// return Convert.FromBase64String(result.Signature);
}
/// <summary>
/// Remote verification via TSP (stub implementation).
/// </summary>
public async Task<bool> RemoteVerifyAsync(
byte[] data,
byte[] signature,
string algorithmId,
EidasKeyConfig keyConfig,
CancellationToken cancellationToken)
{
_logger.LogDebug("TSP remote verification request: keyId={KeyId}, algorithm={Algorithm}",
keyConfig.KeyId, algorithmId);
_logger.LogWarning("Using stub TSP verification - replace with actual TSP API call in production");
// Stub: Always return true
await Task.Delay(50, cancellationToken); // Simulate network latency
_logger.LogInformation("TSP remote verification complete (stub): keyId={KeyId}, valid=true",
keyConfig.KeyId);
return true;
// Production implementation would be:
// var hash = SHA256.HashData(data);
// var request = new
// {
// keyId = keyConfig.KeyId,
// algorithm = algorithmId,
// dataHash = Convert.ToBase64String(hash),
// signature = Convert.ToBase64String(signature)
// };
//
// var response = await _httpClient.PostAsJsonAsync("/api/v1/verify", request, cancellationToken);
// response.EnsureSuccessStatusCode();
//
// var result = await response.Content.ReadFromJsonAsync<TspVerifyResponse>(cancellationToken);
// return result.Valid;
}
}
// DTOs for TSP API (vendor-specific, examples only)
internal record TspSignResponse(string Signature, string Certificate, string Timestamp);
internal record TspVerifyResponse(bool Valid, string? Error);

View File

@@ -0,0 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0002 - eIDAS Crypto Plugin
namespace StellaOps.Cryptography.Plugin.EIDAS;
// DTOs for TSP API (vendor-specific, examples only)
internal record TspSignResponse(string Signature, string Certificate, string Timestamp);
internal record TspVerifyResponse(bool Valid, string? Error);