namespace StellaOps.Cryptography.Profiles.Ecdsa; using System.Security.Cryptography; using StellaOps.Cryptography; using StellaOps.Cryptography.Models; /// /// ECDSA P-256 signer using .NET cryptography (FIPS 186-4 compliant). /// public sealed class EcdsaP256Signer : IContentSigner { private readonly ECDsa _ecdsa; private readonly string _keyId; private readonly TimeProvider _timeProvider; private bool _disposed; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.EcdsaP256; public string Algorithm => "ES256"; public EcdsaP256Signer(string keyId, ECDsa ecdsa, TimeProvider? timeProvider = null) { _keyId = keyId ?? throw new ArgumentNullException(nameof(keyId)); _ecdsa = ecdsa ?? throw new ArgumentNullException(nameof(ecdsa)); _timeProvider = timeProvider ?? TimeProvider.System; if (_ecdsa.KeySize != 256) throw new ArgumentException("ECDSA key must be P-256 (256 bits)", nameof(ecdsa)); } public static EcdsaP256Signer Generate(string keyId, TimeProvider? timeProvider = null) { var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256); return new EcdsaP256Signer(keyId, ecdsa, timeProvider); } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ObjectDisposedException.ThrowIf(_disposed, this); ct.ThrowIfCancellationRequested(); var signature = _ecdsa.SignData(payload.Span, HashAlgorithmName.SHA256); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature, SignedAt = _timeProvider.GetUtcNow() }); } public byte[]? GetPublicKey() { ObjectDisposedException.ThrowIf(_disposed, this); return _ecdsa.ExportSubjectPublicKeyInfo(); } public void Dispose() { if (_disposed) return; _ecdsa?.Dispose(); _disposed = true; } }