// 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; using StellaOps.Cryptography.Plugin.EIDAS.Configuration; namespace StellaOps.Cryptography.Plugin.EIDAS; /// /// eIDAS-compliant crypto provider for European digital signatures. /// Supports QES (Qualified), AES (Advanced), and AdES (Standard) signature levels /// per Regulation (EU) No 910/2014. /// public class EidasCryptoProvider : ICryptoProvider { public string Name => "eidas"; private readonly ILogger _logger; private readonly EidasOptions _options; private readonly TrustServiceProviderClient _tspClient; private readonly LocalEidasProvider _localProvider; private readonly Dictionary _signingKeys = new(); public EidasCryptoProvider( ILogger logger, IOptions options, TrustServiceProviderClient tspClient, LocalEidasProvider localProvider) { _logger = logger; _options = options.Value; _tspClient = tspClient; _localProvider = localProvider; } public bool Supports(CryptoCapability capability, string algorithmId) { // eIDAS provider supports signing and verification only if (capability is not (CryptoCapability.Signing or CryptoCapability.Verification)) { return false; } // Supported algorithms: ECDSA-P256/384/521, RSA-PSS-2048/4096, EdDSA-Ed25519/448 return algorithmId switch { "ECDSA-P256" or "ECDSA-P384" or "ECDSA-P521" => true, "RSA-PSS-2048" or "RSA-PSS-4096" => true, "EdDSA-Ed25519" or "EdDSA-Ed448" => true, _ => false }; } public IPasswordHasher GetPasswordHasher(string algorithmId) { throw new NotSupportedException("eIDAS plugin does not support password hashing"); } public ICryptoHasher GetHasher(string algorithmId) { throw new NotSupportedException("eIDAS plugin does not support content hashing - use BouncyCastle provider"); } public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference) { // Return an eIDAS signer that routes to TSP or local provider return new EidasSigner(_logger, _options, _tspClient, _localProvider, algorithmId, keyReference); } public void UpsertSigningKey(CryptoSigningKey signingKey) { _signingKeys[signingKey.Reference.KeyId] = signingKey; _logger.LogInformation("eIDAS signing key upserted: keyId={KeyId}", signingKey.Reference.KeyId); } public bool RemoveSigningKey(string keyId) { var removed = _signingKeys.Remove(keyId); if (removed) { _logger.LogInformation("eIDAS signing key removed: keyId={KeyId}", keyId); } return removed; } public IReadOnlyCollection GetSigningKeys() { return _signingKeys.Values.ToList().AsReadOnly(); } } /// /// eIDAS signer implementation that routes to TSP or local provider. /// 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 SignAsync(ReadOnlyMemory 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 VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory 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 }; } }