Files
git.stella-ops.org/src/Cryptography/StellaOps.Cryptography.Profiles.Ecdsa/EcdsaP256Signer.cs
2026-01-06 19:07:48 +02:00

67 lines
2.1 KiB
C#

namespace StellaOps.Cryptography.Profiles.Ecdsa;
using System.Security.Cryptography;
using StellaOps.Cryptography;
using StellaOps.Cryptography.Models;
/// <summary>
/// ECDSA P-256 signer using .NET cryptography (FIPS 186-4 compliant).
/// </summary>
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<SignatureResult> SignAsync(ReadOnlyMemory<byte> 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;
}
}