Using statement on RSA class causes 401 / object being disposed prematurely?

739 views Asked by At

I'm currently running into an issue with decrypting jwts using a public key. When I use the RSA class with a using declaration (I did try the older syntax too just to be safe), I get 401s every time. Removing the using keyword fixes the problem, but I'm sure that's poor practice as RSA is an IDisposable. Any ideas on how to make this work the proper way?

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt =>
{
    var publicKey = tokenSettings.PublicKey.ToByteArray();
        
    // removing "using" makes it work
    using var rsa = RSA.Create();
    rsa.ImportSubjectPublicKeyInfo(publicKey, out _);
    var key = new RsaSecurityKey(rsa);
            
    opt.TokenValidationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = key,
        ValidAudience = tokenSettings.TargetAud,
        ValidIssuers = new[] { tokenSettings.Issuer },
        RequireSignedTokens = true,
        RequireExpirationTime = true,
        ValidateLifetime = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ClockSkew = TimeSpan.Zero,     
    };
});

I did find a self-answer here RSA Disposed Object Error - every other test, but isn't omitting "using" an incorrect use of IDisposable objects?

I'll also note this is not the same as the caching issue in which it returns 200 once and 401 after that. The code currently returns 401 every time.

3

There are 3 answers

0
Damien_The_Unbeliever On

You Dispose objects when you're sure you no longer require them.

Here, you may be issuing tokens at any time that your application is running. So Dispose-ing of the object whilst still configuring the application is wrong.

I'd suggest, here, that you don't Dispose the object. The only time you actually know that it's no longer required is when the application is shutting down - but in that case, you know that everything in the process is about to be destroyed - there's no point delaying that just to do a bit of tidying that nobody will benefit from.

2
Ben Wong On
using var rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(publicKey, out _);
var key = new RsaSecurityKey(rsa.exportParameters(false));

Source: https://www.reddit.com/r/dotnet/comments/ihno7b/-/g31aze8

1
Stefan Steiger On

How about setting the using-clause in curly brackets, so it doesn't get disposed after the next instruction ?

services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt =>
{
    System.ReadOnlySpan<byte> publicKey = tokenSettings.PublicKey.ToByteArray();

    // removing "using" makes it work
    using (System.Security.Cryptography.RSA rsa = System.Security.Cryptography.RSA.Create())
    {
        rsa.ImportSubjectPublicKeyInfo(publicKey, out _);
        Microsoft.IdentityModel.Tokens.RsaSecurityKey key = new Microsoft.IdentityModel.Tokens.RsaSecurityKey(rsa);

        opt.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            IssuerSigningKey = key,
            ValidAudience = "tokenSettings.TargetAud",
            ValidIssuers = new[] { "tokenSettings.Issuer" },
            RequireSignedTokens = true,
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ValidateAudience = true,
            ValidateIssuer = true,
            ClockSkew = System.TimeSpan.Zero,
        };
    }
});

But most-likely, this is because RsaSecurityKey has the instance of the RSA-Provider contained, and uses that to encrypt and decrypt the JWT-Bearer. Which is a bit of a problem if the RSA instance is disposed ...

//
// Zusammenfassung:
//     Represents a Rsa security key.
public class RsaSecurityKey : AsymmetricSecurityKey
{
    public RsaSecurityKey(RSAParameters rsaParameters);
    public RsaSecurityKey(RSA rsa);

    [Obsolete("HasPrivateKey method is deprecated, please use PrivateKeyStatus.")]
    public override bool HasPrivateKey { get; }
    public override PrivateKeyStatus PrivateKeyStatus { get; }


    public RSA Rsa { get; }
}

Just remove the using, the RSA-provider probably has application-lifetime scope. However that works with MultiThreading...