如何仅用Modulus和Exponent生成Azure AD B2C公钥以验证ID Token签名?
解决Azure AD B2C的RSA公钥生成与JWT签名验证问题
我明白你现在的困扰——手里只有Azure AD B2C返回的n(模数)和e(指数),不知道怎么转成可用的RSA公钥,还要用C#实现,同时想在jwt.io上测试签名。下面我一步步给你讲清楚怎么做:
一、把n和e转换成PEM格式的公钥(用于jwt.io测试)
Azure AD B2C返回的n和e是Base64Url编码的,不是普通的Base64,所以首先要解码它们。然后我们可以用C#的System.Security.Cryptography类库来生成标准的PEM公钥,这样jwt.io就能直接识别。
具体代码实现
using System; using System.Security.Cryptography; using System.Text; public static string GenerateRsaPublicKey(string base64UrlModulus, string base64UrlExponent) { // 解码Base64Url格式的模数和指数 byte[] modulus = Base64UrlDecode(base64UrlModulus); byte[] exponent = Base64UrlDecode(base64UrlExponent); // 创建RSAParameters对象并赋值 var rsaParams = new RSAParameters { Modulus = modulus, Exponent = exponent }; // 导入参数并导出PEM格式公钥 using (var rsa = RSA.Create()) { rsa.ImportParameters(rsaParams); var xmlPublicKey = rsa.ToXmlString(includePrivateParameters: false); // 将XML格式转换为标准PEM格式 var base64Content = xmlPublicKey .Replace("<RSAKeyValue>", "") .Replace("</RSAKeyValue>", "") .Replace("<Modulus>", "") .Replace("</Modulus>", "") .Replace("<Exponent>", "") .Replace("</Exponent>", ""); // 按PEM标准分割为每行64字符 var pemBuilder = new StringBuilder(); pemBuilder.AppendLine("-----BEGIN PUBLIC KEY-----"); for (int i = 0; i < base64Content.Length; i += 64) { pemBuilder.AppendLine(base64Content.Substring(i, Math.Min(64, base64Content.Length - i))); } pemBuilder.AppendLine("-----END PUBLIC KEY-----"); return pemBuilder.ToString(); } } // 实现Base64Url解码(处理'-'和'_'替换为'+'和'/',补全'=') private static byte[] Base64UrlDecode(string input) { input = input.Replace('-', '+').Replace('_', '/'); switch (input.Length % 4) { case 2: input += "=="; break; case 3: input += "="; break; } return Convert.FromBase64String(input); }
调用这个方法时,把你拿到的n和e值传进去,就能得到标准的PEM公钥,直接复制到jwt.io的「Public Key」区域就能测试签名了。
二、用C#验证Azure AD B2C的id_token签名
既然你已经有验证库的思路,我推荐用微软官方的Microsoft.IdentityModel.Tokens库(NuGet可以直接安装),它能自动处理密钥获取和签名验证,不用手动生成公钥也能搞定,更适合生产环境:
示例代码
using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; using System.Threading.Tasks; public static async Task<bool> ValidateIdTokenAsync(string idToken, string metadataEndpoint, string clientId) { var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>( metadataEndpoint, new OpenIdConnectConfigurationRetriever()); var config = await configurationManager.GetConfigurationAsync(); var validationParameters = new TokenValidationParameters { ValidAudience = clientId, // 你的Azure AD B2C应用的Client ID ValidIssuer = config.Issuer, // 从元数据自动获取的Issuer URI IssuerSigningKeys = config.SigningKeys, ValidateLifetime = true, ValidateAudience = true, ValidateIssuer = true }; try { var handler = new JwtSecurityTokenHandler(); handler.ValidateToken(idToken, validationParameters, out var validatedToken); return true; } catch (Exception ex) { // 验证失败,处理异常(比如日志记录) Console.WriteLine($"验证失败:{ex.Message}"); return false; } }
这个方法会自动从元数据端点获取对应kid的密钥(包括n和e),然后完成签名验证,比手动生成公钥更可靠,避免自己处理编码错误。
三、jwt.io测试小贴士
- 把你的
id_token粘贴到jwt.io的「Encoded」区域 - 把刚才用C#生成的PEM公钥粘贴到「Public Key」区域
- 确认「Signature Verified」显示为绿色,就说明签名有效了
如果遇到问题,检查一下kid是否匹配——确保你用的密钥和id_token头里的kid一致,Azure AD B2C可能会有多个密钥,要选对应kid的那组n和e。
内容的提问来源于stack exchange,提问作者Lukas Méndez Duus
相关产品推荐
相关产品推荐

