You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8中使用JwtSecurityTokenHandler验证JWT时解决RSA释放错误

问题:RSA对象释放导致JWT签名验证时出现已释放对象错误

我在使用.NET 8.0时遇到一个问题:当用using语句或finally块调用rsa.Dispose()时,约50%的概率会触发“无法访问已释放的对象(Microsoft.IdentityModel.Tokens.RsaSecurityKey)”错误;但如果不手动释放RSA对象,就不会出现这个错误。

问题代码

RSA rsa = RSA.Create(); // OR using(RSA rsa = RSA.Create())
try
{
    rsa.ImportParameters(new RSAParameters
    {
        Modulus = bytesN, 
        Exponent = bytesE 
    }); 

    var rsaSecurityKey = new RsaSecurityKey(rsa)
    {
        KeyId = strKid 
    };

    JwtConfiguration jwtConfiguration = _configurationUtil.GetJwtConfiguration("Kakao");

    var tokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = jwtConfiguration.ValidateIssuer, 
        ValidIssuer = jwtConfiguration.ValidIssuer,
        ValidateAudience = jwtConfiguration.ValidateAudience, 
        ValidAudience = jwtConfiguration.ValidAudience, 
        ValidateLifetime = jwtConfiguration.ValidateLifetime,
        RequireExpirationTime = jwtConfiguration.RequireExpirationTime, 
        ValidateIssuerSigningKey = jwtConfiguration.ValidateIssuerSigningKey, 
        IssuerSigningKey = rsaSecurityKey,
        TryAllIssuerSigningKeys = jwtConfiguration.TryAllIssuerSigningKeys, 
    };

    var tokenHandler = new JwtSecurityTokenHandler(); 

    var claimsPrincipal = tokenHandler.ValidateToken(strIdToken, tokenValidationParameters, out SecurityToken validatedToken);
    var jwtSecurityToken = validatedToken as JwtSecurityToken; 

    string nonce = jwtSecurityToken!.Claims.FirstOrDefault(c => c.Type == "nonce")?.Value!;
    if (nonce != oauthConfiguration.Nonce)
        throw new Exception("NONCE ERROR");

    strId = jwtSecurityToken.Claims.FirstOrDefault(c => c.Type == "sub")?.Value!; 
    strEmail = jwtSecurityToken.Claims.FirstOrDefault(c => c.Type == "email")?.Value!; 
}
catch (Exception e)
{
    response.RESULT_CODE = "F000";
    response.RESULT_MSG = e.Message; 
    return Page(); 
}
finally
{
    rsa.Dispose();
}

错误信息

IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'sameKid', InternalId: 'internalId'. , KeyId: sameKid
'. 
Number of keys in TokenValidationParameters: '1'. 
Number of keys in Configuration: '0'. 
Matched key was in 'TokenValidationParameters'. 
kid: 'sameKid'. 
Exceptions caught:
 'System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Security.Cryptography.RSABCrypt'.
   at System.Security.Cryptography.RSABCrypt.GetKey()
   at System.Security.Cryptography.RSABCrypt.VerifyHash(ReadOnlySpan`1 hash, ReadOnlySpan`1 signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.VerifyUsingSpan(Boolean isRSA, ReadOnlySpan`1 bytes, Byte[] signature)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.VerifyRsa(Byte[] bytes, Byte[] signature)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.Verify(Byte[] bytes, Byte[] signature)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.Verify(Byte[] input, Byte[] signature)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
'.
token: '[Security Artifact of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden.]'.

我试过用using和finally确保RSA对象释放,也试过不释放来规避错误,希望签名密钥始终有效,验证过程能正常完成,但不知道为什么RSA对象会被提前释放,请问该怎么正确管理RSA对象的释放?


解决方案

核心原因

RsaSecurityKey默认不会复制传入的RSA对象,而是直接持有对它的引用。JWT签名验证的内部流程可能存在延迟执行的情况(比如缓存逻辑、异步操作),当你在finally里提前释放RSA对象后,验证过程再去访问它就会触发已释放对象错误。50%的概率是因为验证操作的执行时机不确定——有时候在释放前完成,有时候在释放后才执行。

正确处理方式

1. 让RsaSecurityKey接管RSA对象生命周期

创建RsaSecurityKey时,传入第二个参数willBeDisposed并设为true,这样RsaSecurityKey会负责释放底层的RSA对象,你无需手动调用Dispose():

var rsaSecurityKey = new RsaSecurityKey(rsa, willBeDisposed: true)
{
    KeyId = strKid 
};

之后可以去掉finally里的rsa.Dispose(),也不要用using包裹RSA对象,交给RsaSecurityKey管理释放。

2. 直接用RSAParameters创建RsaSecurityKey

跳过手动创建RSA对象的步骤,直接用RSAParameters实例化RsaSecurityKey,这样RsaSecurityKey会自行创建内部的RSA实例,完全不依赖外部对象:

var rsaSecurityKey = new RsaSecurityKey(new RSAParameters
{
    Modulus = bytesN, 
    Exponent = bytesE 
})
{
    KeyId = strKid 
};

这种方式从根源上避免了引用释放的问题,RsaSecurityKey会自行处理内部加密对象的生命周期。

3. 确保验证完全完成后再释放

如果必须手动控制RSA对象释放,要确认tokenHandler.ValidateToken()及所有后续依赖操作完全执行完毕后,再调用Dispose()。不过这种方式需要确保验证过程没有异步延迟,可靠性不如前两种方法。


内容的提问来源于stack exchange,提问作者khoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:37:34