ASP.NET应用中使用OpenSSL RSA密钥实现JWT签名遇阻求助
问题:如何使用OpenSSL生成的RSA密钥正确创建JWT令牌?
生成密钥的OpenSSL命令
openssl genrsa -out keypair-2023.pem 4096 openssl rsa -in keypair-2023.pem -pubout -out pub-2023.crt openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair-2023.pem -out priv-2023-v8.key
密钥文件格式
-----BEGIN PUBLIC KEY----- {{ public key contents }} -----END PUBLIC KEY----- -----BEGIN PRIVATE KEY----- {{ private key contents }} -----END PRIVATE KEY-----
尝试的代码及错误
代码片段1
var rsaKey = RSA.Create(); rsaKey.ImportFromPem(privateKeyStr); rsaKey.ImportFromPem(publicKeyStr); var rsaParams = rsaKey.ExportParameters(true); var securityKey = new RsaSecurityKey(rsaParams);
错误:System.Security.Cryptography.CryptographicException: 'Key does not exist.'(发生在ExportParameters行)
代码片段2
var privateKeyBuffer = new Span<byte>(new byte[privateKeyStr.Length]); Convert.TryFromBase64String(privateKeyStr, privateKeyBuffer, out _); var rsaKey = RSA.Create(); rsaKey.ImportRSAPrivateKey(privateKeyBuffer, out _); rsaKey.ImportFromPem(publicKeyStr); var rsaParams = rsaKey.ExportParameters(true);
错误:AsnContentException: The provided data is tagged with 'Universal' class value '0', but it should have been 'Universal' class value '16'(发生在ImportRSAPrivateKey行)
将ImportRSAPrivateKey替换为ImportPkcs8PrivateKey会抛出相同异常。
代码片段3(用于JWT签名)
var rsaKey = RSA.Create(); rsaKey.ImportFromPem(privateKeyStr); rsaKey.ImportFromPem(publicKeyStr); var securityKey = new RsaSecurityKey(rsaKey); _signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha512); _securityTokenHandler = new JwtSecurityTokenHandler();
错误:应用可启动,但创建JWT令牌时抛出System.Security.Cryptography.CryptographicException: 'Key does not exist.'
解决方案
核心问题分析
- 重复导入密钥覆盖私钥:同一个
RSA实例先导入私钥再导入公钥,会覆盖私钥信息,导致后续需要私钥的操作(如ExportParameters(true)、JWT签名)失败。 - 错误处理PEM字符串:直接将包含PEM头/尾的完整字符串转换为Base64是错误的,
ImportRSAPrivateKey/ImportPkcs8PrivateKey需要的是去除格式后的原始ASN.1字节,而ImportFromPem可以直接处理完整的PEM格式字符串。
正确实现代码
1. 创建JWT签名凭证(使用私钥)
// 确保privateKeyStr包含完整的PEM格式(包括-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----) using var rsaKey = RSA.Create(); rsaKey.ImportFromPem(privateKeyStr); // 私钥已包含公钥信息,无需单独导入公钥 var securityKey = new RsaSecurityKey(rsaKey); _signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha512); _securityTokenHandler = new JwtSecurityTokenHandler();
2. 使用公钥验证JWT令牌
// 确保publicKeyStr包含完整的PEM格式 using var rsaPublicKey = RSA.Create(); rsaPublicKey.ImportFromPem(publicKeyStr); var validationParams = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidIssuer = "你的发行方标识", ValidAudience = "你的受众标识", IssuerSigningKey = new RsaSecurityKey(rsaPublicKey) }; // 执行验证 _securityTokenHandler.ValidateToken(jwtToken, validationParams, out var validatedToken);
关键注意事项
- 私钥文件是PKCS#8格式(
-----BEGIN PRIVATE KEY-----),ImportFromPem可以直接识别并加载,无需额外转换。 - 签名操作只需要私钥,公钥仅用于验证,不需要在签名时导入。
- 始终使用
using语句管理RSA实例,确保资源正确释放。
内容的提问来源于stack exchange,提问作者Trec Apps
相关产品推荐
相关产品推荐

