Restructure solution layout by module
This commit is contained in:
82
src/__Libraries/StellaOps.Cryptography/EcdsaSigner.cs
Normal file
82
src/__Libraries/StellaOps.Cryptography/EcdsaSigner.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace StellaOps.Cryptography;
|
||||
|
||||
internal sealed class EcdsaSigner : ICryptoSigner
|
||||
{
|
||||
private static readonly string[] DefaultKeyOps = { "sign", "verify" };
|
||||
private readonly CryptoSigningKey signingKey;
|
||||
|
||||
private EcdsaSigner(CryptoSigningKey signingKey)
|
||||
=> this.signingKey = signingKey ?? throw new ArgumentNullException(nameof(signingKey));
|
||||
|
||||
public string KeyId => signingKey.Reference.KeyId;
|
||||
|
||||
public string AlgorithmId => signingKey.AlgorithmId;
|
||||
|
||||
public static ICryptoSigner Create(CryptoSigningKey signingKey) => new EcdsaSigner(signingKey);
|
||||
|
||||
public ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using var ecdsa = ECDsa.Create(signingKey.PrivateParameters);
|
||||
var hashAlgorithm = ResolveHashAlgorithm(signingKey.AlgorithmId);
|
||||
var signature = ecdsa.SignData(data.Span, hashAlgorithm);
|
||||
return ValueTask.FromResult(signature);
|
||||
}
|
||||
|
||||
public ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using var ecdsa = ECDsa.Create(signingKey.PublicParameters);
|
||||
var hashAlgorithm = ResolveHashAlgorithm(signingKey.AlgorithmId);
|
||||
var verified = ecdsa.VerifyData(data.Span, signature.Span, hashAlgorithm);
|
||||
return ValueTask.FromResult(verified);
|
||||
}
|
||||
|
||||
public JsonWebKey ExportPublicJsonWebKey()
|
||||
{
|
||||
var jwk = new JsonWebKey
|
||||
{
|
||||
Kid = signingKey.Reference.KeyId,
|
||||
Alg = signingKey.AlgorithmId,
|
||||
Kty = JsonWebAlgorithmsKeyTypes.EllipticCurve,
|
||||
Use = JsonWebKeyUseNames.Sig,
|
||||
Crv = ResolveCurve(signingKey.AlgorithmId)
|
||||
};
|
||||
|
||||
foreach (var op in DefaultKeyOps)
|
||||
{
|
||||
jwk.KeyOps.Add(op);
|
||||
}
|
||||
|
||||
jwk.X = Base64UrlEncoder.Encode(signingKey.PublicParameters.Q.X ?? Array.Empty<byte>());
|
||||
jwk.Y = Base64UrlEncoder.Encode(signingKey.PublicParameters.Q.Y ?? Array.Empty<byte>());
|
||||
|
||||
return jwk;
|
||||
}
|
||||
|
||||
private static HashAlgorithmName ResolveHashAlgorithm(string algorithmId) =>
|
||||
algorithmId switch
|
||||
{
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es256, StringComparison.OrdinalIgnoreCase) => HashAlgorithmName.SHA256,
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es384, StringComparison.OrdinalIgnoreCase) => HashAlgorithmName.SHA384,
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es512, StringComparison.OrdinalIgnoreCase) => HashAlgorithmName.SHA512,
|
||||
_ => throw new InvalidOperationException($"Unsupported ECDSA signing algorithm '{algorithmId}'.")
|
||||
};
|
||||
|
||||
private static string ResolveCurve(string algorithmId)
|
||||
=> algorithmId switch
|
||||
{
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es256, StringComparison.OrdinalIgnoreCase) => JsonWebKeyECTypes.P256,
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es384, StringComparison.OrdinalIgnoreCase) => JsonWebKeyECTypes.P384,
|
||||
{ } alg when string.Equals(alg, SignatureAlgorithms.Es512, StringComparison.OrdinalIgnoreCase) => JsonWebKeyECTypes.P521,
|
||||
_ => throw new InvalidOperationException($"Unsupported ECDSA curve mapping for algorithm '{algorithmId}'.")
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user