Files
tiku-backend.net/Tiku.Infrastructure/Auth/JwtKeyRing.cs

60 lines
1.8 KiB
C#

using System.Security.Cryptography;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Tiku.Application.Security;
namespace Tiku.Infrastructure.Auth;
internal sealed class JwtKeyRing : IJwtKeyRing, IDisposable
{
private readonly List<RSA> keys = [];
public JwtKeyRing(IOptions<JwtOptions> options)
{
var value = options.Value;
var signingRsa = RSA.Create(3072);
keys.Add(signingRsa);
if (!string.IsNullOrWhiteSpace(value.PrivateKeyPem))
{
signingRsa.ImportFromPem(value.PrivateKeyPem);
}
var signingKey = CreateKey(signingRsa, value.KeyId);
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
var validationKeys = new List<SecurityKey> { signingKey };
foreach (var pair in value.PublicKeys.Where(pair => pair.Key != value.KeyId))
{
var rsa = RSA.Create();
rsa.ImportFromPem(pair.Value);
keys.Add(rsa);
validationKeys.Add(CreateKey(rsa, pair.Key));
}
ValidationKeys = validationKeys;
}
public SigningCredentials SigningCredentials { get; }
public IReadOnlyCollection<SecurityKey> ValidationKeys { get; }
private static RsaSecurityKey CreateKey(RSA rsa, string keyId) => new(rsa)
{
KeyId = keyId,
// IdentityModel caches signature providers globally by key identity. A key ring owns
// and disposes its RSA instances, so a provider retained by another in-process host
// could otherwise reference an RSA instance that has already been disposed.
CryptoProviderFactory = new CryptoProviderFactory
{
CacheSignatureProviders = false
}
};
public void Dispose()
{
foreach (var key in keys)
{
key.Dispose();
}
}
}