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

.NET 6中Python jwe.decrypt的等效JWT解密实现求助

.NET 6中解密JWE格式JWT令牌的正确实现

问题背景

我正在.NET 6中尝试解密JWE格式的JWT令牌,但始终无法成功。解密密钥为PEM编码的私钥,格式示例:

-----BEGIN PRIVATE KEY-----
                ...
-----END PRIVATE KEY-----

已有一段可正常运行的Python解密代码,现寻求对应的.NET 6等效实现。

参考Python代码

from jose import jwe, jwt, jwk, jws
import base64

encrypted_jwe = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIn0.IDqmx1u_8Jd6XofUyTubUEAMWw9Zu2reF3JJsijRICCFRG_Dcb15pOs61DQx8DwhQbVSWA-Zbt_xlJ5dTtaC_JlP8PRFYbAkhHdOtPVUuEKIx9OEm5DOF5hi-d-VJd5JOrDaGgSSlz59vQHgY8whGzNTbJhfjyKwkz-uaEiyb8kblDAVd-mjBPcL1_1MOpMfyfuXgroi3RiR--zyjNgOUZ2R3hKZmlISAuRjrIJB8xKZntRu3GtJczX6jCgJFvWAYqrlHtMoRLuGrcOsM3WDx7urX9ur90A7idGuq2rj8aaOg2Ld-sAMfhoS0jKwK4Gj0CumAza4QvpZPVjVWaep7A.XlSCfsZVmku1CGxK.mCbCvPxsdWRDw_ZmlMtETrX_TLdxQQ0lxqFfUaX6Pv8TCChFodZb9PE0kUycW1pTTPYYbGh2d40CYgvLAMP_c1me27uFYOsy3P3CDoULKR-MNU4f19nCuRlMgWeMqG0e7GVmGQwem4TpcA4CZTdd5V0kzXBLm4ns3i3NlNoNrhfG7pW87Eb0oYo9s1wvALDmvgiFUb-apxUqJXaSIZ51pSSwkyPSLPtd_tq-8weNNuyf9SBsQUWEjRn8CHtmjuKhKOP4TaJYqjsA80ydLUUhmogrMzpqEgahitZ1ygDwv2UYwzykt5kklZ63OGPdZrYosM3t0kpi4CP9F5gyvx-jOiiMLcdz46d0t8315H9HEJW_Du3BkmW6IQ1r__mlrp2ylNMTVT7_oh_WQHUc2HEszJ-yELUSd0RM96vgXUGjWlwfex9hLYI2VaMSZ00MsarEl3HEUFnsTYIqeD8hLuJjeUGH_EXJRqh6n2-hdApO67IPRE3Eoy4rpAPZEsHrGP08WfFicjRr49PEaJlnsxVXsTxaO6smKWiCwh4JiFDYy7y-kw9orvRqyvR_8aVh1XhNFktUhtyRBOSXqN-nGxPnjD2C3lJgQAOck2i8lt_gcEiY2xqGiS0Qz-6119cRr3R2PkEXMFoVRL_8gK94OckenUg-OmIWydfJH66c_RaSGEsgozOjHCAiSiA0N2kzH_juBsLxH4CwR9VYm0lVitLVnoauVloz3fdm-so7uJ5ccFPPbfhCdKIOTwOSYxjVBJh_QWOz-BcfOeCfXIxP9PZ7DTsn2Esz2zT-hQgElOy01j0_rYfNOj92EEqRjbWGdtSr4onkAsQqIFhKJlSFQ7TbemgO6cliRWmB2Hje4XVJSVJBAv-qOh397sgCm4QYJknue0XejvIFSxaxGaoockQsA8VTl1Wtpj_Up1alKpm1tyIwVOEuQCdpy5XreSI3KxnnkKS17Ged1IT9RTL-F6X8xvgoig9rp00T9YnrXB4yNsLHkPkYHFPI4IN_O9ULrDSg_p6lN6ulc2DNY0ap-z9VSnOwKZuqnxyXxw5ymvraieRC.0fJ38Gr299EfDubtBpuRYg"
decryption_key = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ1pSS08rckhWeFBuRFkKN2Nndk1PT2lyMGVyb1NRLy8rUnhJN05CV1NraURlNCtDUFp5NzYyUlNFZ20yeHY4SGc0dmw1MnFMSzk3STd2Ngp1QnlIUlNtQU96dnduQUkrQXJkVVd3bG93T1FGSFRHQ0FQK1JrdExHOWp3VTlaK2ZBNFJaQTdvMm5Gd2ZzVDBjClBBZ3BSODlja2JkYXdtbWtDUHYxbTQ3M2hnUkNyNytRVWJoR2VWSkFVZU84SHBmOGozRmJIdHBwdTJnR1pwcnEKZnIzUUh2UW9JUFRqOVFwN1poaDhuOXpIQnd2TzBxMHRsbDJPV0V4YXh3Q2RwZ0ZUVUJiamZQWTd4Z0hqVk1TeQpBQTBocW1hcWNOdFpWdFU3MWdoYnVlOW02T1JNajhBTThmc3ZUdEFlZWdJcG9uYVBxM1RBTjVnTTRjMHFGOVJDCnZHSEt0MW5YQWdNQkFBRUNnZ0VBQ25ORUtPbVFYbWJRQWZLQWpqc3FvanFLUy9rQ1JEelo0aDc1aDNDaFhkbU8KVDN1RDlrVkNVK0FXVDFQc1BnMzhrS0VwVm1PU21YWlB5R0lLUyt6YXpndjc0OFYzWDZ3R3ZRRXlESXJ1WVN6aQo1Y2JVcGlQWkxFaTR3d01XWFp5N1J1aDZwMk9VNG5KT3hPQ1ErK09TRDNFK0M3bXF3MHo3aFAwVXkrcSsycDhQCkVOcUhPeVVyVlphTVZySjk2VVFGTXFtTGl3R2xVM2NTemlGRHlLYlpGK1RDU0kydnhMbFhRNnhtMyt5R0ZGTHEKWTAxelBFTk5BSzJ2WXUrdlNaYTVIYmpuNzFDQndSY2J1bUJYWm5PVWdLdk9XWVBEVEQwYWhSWlczVWhwWDZTbgpSTUREdUhwWDZyNjZLSTZnUy9kZ09RSHo4YTcvUVZvZm1sTmxsY21yZVFLQmdRREh6ckREeS9XMU5KczFOVCt1ClJ0M1Q2ZmZSelJzdUYzemVtL3FxUnlzbXpOWi9JWFMwZGZMdkprZmJZVVpYdVYzekxKM2ZOK0hqK2wyb1dsajQKVUVKNkJRNmtLMTA4TlJlRDdxQUNWTDBVYlhWUFRrWWZnOGFTYzNWVTNvSk1pUGM0WDNvRkZZenVOSnNqMThZTwpmVVh1a0cyNVIvdkV3NFZJeHlBZFlEWFJ5UUtCZ1FERVgxQlgzZHhNcHp2Yk9rRlVyeFNyM2x5ZDJWNzdrUnRHCm54NXJtQ2ZBQmRuMkN1cmdkNjMvdHlBbElMSVEwUGRWNEkzSjhyOVNsRGd2c2RaMS9ZWXE3Qjg1QzE4QUZRWjMKTUlJcno3VHNCTU4xZ29sMWhvbjEweUxWZ1h0RCtYSmhSU1JLZVlyY2pVZ2s2YzVnL2NNbldaSk5Qck5IQnZabgpYVTNYcTFDZW53S0JnQmh4N3lwbFA3Qmx1VDl2UGxWQ0VrMms2RTdndXh5T2Z4WjFHdHZPVFRjNEJGNFZaOFBVClVsNXozb0syaWZhZGxTTDFLSE53ME5BbGgyUGJlZHNYWnFxckI3bUtIS1FMRXM5dldHQXdvZGxuOHR3N0JWVkIKdC8rZTVKSDNqeFZ5QkZ6L1FWeHg0SVljSlliakZUWWNtWHV3YTl3cE5HSkVVWUF2N0h2cmsyb1JBb0dCQUlueQpRdWhXckNrVTJaRjViT2RtVG1rbHZYNE5jeEhacjRWK3hjbmppeGp6ZzFlNTk2RlRzV0dQYlA5bjVtNjF2T29RCjRzbll3dG5wMFVaLzhSM1Z5T2hpRXRUcHpnU3F3RzMrSDVOVFI4NzgwYW04MTM4OTBiQmtiM1g0alhVRXlIclkKTFZRNzZYWmhzU3B0TEZNcU1HdDJHclkwczQvVHJraVNYTnFDT0JJVkFvR0FlZlhMMWJmUExnOVFKRlBLUmNZcgoycWRFRzFWRC8vSFF6K3lQaTlscVB3eGM3aGwreTcvcXMxQW1peUFFK2xESEJadEMwTGJ2Si9XNWR5K1ZtZFNWCkpvRkFXS1RUdTdORHZ4dTJ3RHNFRWVnZ3AxVnBId3ZzUzRDS2ZDa0l6ZlBzTFhyMTBFSExSSW43MXByKzdEa3oKSXkxdjZ0UkZONldFdHBHVzh4T0VOeEk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

decrypted_token = jwe.decrypt(encrypted_jwe, base64.urlsafe_b64decode(decryption_key))

我尝试的.NET 6代码(报错)

运行时出现**“输入超出RSA密码长度”**错误:

using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Crypto.Engines;
using System.IdentityModel.Tokens.Jwt;
using AesGcm = System.Security.Cryptography.AesGcm;

static string DecryptJwe2(string jweToken, string decryptionKey)
{
    var privateKeyPemBytes = Convert.FromBase64String(decryptionKey);
    var privateKeyPem = Encoding.UTF8.GetString(privateKeyPemBytes);

    var jweTokenBytes = Convert.FromBase64String(SafeBase64String(jweToken));
    jweToken = Encoding.UTF8.GetString(jweTokenBytes);

    // 导入私钥
    PemReader pemReader = new PemReader(new StringReader(privateKeyPem));
    var keyPair = pemReader.ReadObject();

    // RSA解密
    var decryptor = new RsaEngine();
    decryptor.Init(false, (Org.BouncyCastle.Crypto.ICipherParameters)keyPair);

    byte[] decodedToken = Encoding.UTF8.GetBytes(jweToken);
    byte[] decryptedToken = decryptor.ProcessBlock(decodedToken, 0, decodedToken.Length); // 此处报错

    return Encoding.UTF8.GetString(decryptedToken);
}

static string SafeBase64String(string base64String)
{
    base64String = base64String.Replace('-', '+').Replace('_', '/');
    int padding = base64String.Length % 4;
    if (padding > 0) base64String += new string('=', 4 - padding);
    return base64String;
}

错误原因

直接对整个JWE令牌调用RSA解密是错误的。JWE的解密流程分阶段:先用RSA私钥解密出对称加密密钥,再用该密钥解密实际令牌内容。手动调用RSA引擎无法处理这一复杂流程,且RSA本身有明文长度限制,因此会报“输入超出长度”的错误。

正确的.NET 6实现

使用官方的System.IdentityModel.Tokens.Jwt库处理JWE解密,无需手动操作RSA引擎。先安装以下NuGet包:

Install-Package System.IdentityModel.Tokens.Jwt
Install-Package Microsoft.IdentityModel.Tokens

完整代码实现

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Text;

static string DecryptJwe(string jweToken, string base64EncodedPemKey)
{
    // 解码Base64格式的PEM私钥
    var pemBytes = Convert.FromBase64String(base64EncodedPemKey);
    var pemString = Encoding.UTF8.GetString(pemBytes);

    // 导入RSA私钥
    using var rsa = RSA.Create();
    rsa.ImportFromPem(pemString.ToCharArray());

    // 配置令牌验证参数
    var tokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        TokenDecryptionKey = new RsaSecurityKey(rsa)
    };

    // 处理JWE解密
    var handler = new JwtSecurityTokenHandler();
    handler.ReadJwtToken(jweToken); // 解析令牌结构
    var result = handler.ValidateToken(jweToken, tokenValidationParameters, out _);

    // 返回解密后的令牌内容
    return result.ToString();
}

代码说明

  1. 导入私钥:使用.NET 6内置的RSA.ImportFromPem方法直接导入PEM格式私钥,无需依赖BouncyCastle。
  2. 配置验证参数:设置TokenDecryptionKey为导入的RSA密钥,关闭发行者和受众验证(可根据实际需求调整)。
  3. 解密JWE:JwtSecurityTokenHandler自动处理完整解密流程——用RSA私钥解密出对称密钥,再用对称密钥解密令牌内容,最后返回明文。

内容的提问来源于stack exchange,提问作者João Antunes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 01:05:55