C#参照Java示例生成Security Event Token(SET)实现方法
C# 生成符合规范的安全事件令牌(SET)实现方案
SET本质是带特定头、载荷结构,使用指定算法签名的JWT,对齐你提供的Java示例核心要求如下:
- 签名算法固定使用
PS512(RSASSA-PSS + SHA512) - JWT头的
typ字段必须为secevent+jwt,同时携带kid(公钥唯一标识)和alg字段 - 载荷字段和Java示例完全匹配
下面分别给出使用jose-jwt库、以及微软官方JWT库的两种实现方式。
方案1:使用jose-jwt实现(推荐,逻辑更简洁)
首先通过Nuget安装依赖:
dotnet add package jose-jwt
核心实现代码:
using System; using System.Collections.Generic; using System.Security.Cryptography; using Jose; public string GenerateSET( string issuer, string subject, string transactionId, string eventTargetUrl, string publicKeyId, RSA rsaPrivateKey) { // 构造载荷,和Java示例字段一一对应 var utcNow = DateTimeOffset.UtcNow; var payload = new Dictionary<string, object> { ["iss"] = issuer, ["iat"] = utcNow.ToUnixTimeSeconds(), // 秒级UTC时间戳,和Java默认JWT iat格式一致 ["jti"] = Guid.NewGuid().ToString(), ["sub"] = subject, ["events"] = new Dictionary<string, object> { // 键为事件投递目标URL,值为空对象,和Java Map.of(event, Map.of())逻辑一致 [eventTargetUrl] = new Dictionary<string, object>() }, ["txn"] = transactionId }; // 构造JWT头 var headers = new Dictionary<string, object> { ["typ"] = "secevent+jwt", ["kid"] = publicKeyId, ["alg"] = "PS512" }; // 签名并序列化为Base64字符串 return JWT.Encode( payload: payload, key: rsaPrivateKey, algorithm: JwsAlgorithm.PS512, extraHeaders: headers); }
RSA私钥加载示例(支持PEM格式):
// 从PEM文件加载私钥 string privateKeyText = File.ReadAllText("path/to/your/rsa_private.pem"); RSA rsaKey = RSA.Create(); rsaKey.ImportFromPem(privateKeyText); // 如果是加密的PEM私钥,调用ImportFromEncryptedPem方法传入密码即可
方案2:使用微软官方System.IdentityModel.Tokens.Jwt实现
如果不想用第三方的jose-jwt,可以用微软官方的JWT库,先安装依赖:
dotnet add package System.IdentityModel.Tokens.Jwt
核心实现代码:
using System; using System.Collections.Generic; using System.Security.Cryptography; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; public string GenerateSETWithMsLib( string issuer, string subject, string transactionId, string eventTargetUrl, string publicKeyId, RSA rsaPrivateKey) { var utcNow = DateTime.UtcNow; var securityKey = new RsaSecurityKey(rsaPrivateKey) { KeyId = publicKeyId }; // 指定PS512签名算法 var signingCreds = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSsaPssSha512); var tokenHandler = new JwtSecurityTokenHandler(); var token = new JwtSecurityToken( issuer: issuer, claims: new List<System.Security.Claims.Claim> { new System.Security.Claims.Claim("sub", subject), new System.Security.Claims.Claim("jti", Guid.NewGuid().ToString()), new System.Security.Claims.Claim("txn", transactionId) }, signingCredentials: signingCreds, issuedAt: utcNow ); // 覆盖默认typ头,修正为SET要求的固定值 token.Header["typ"] = "secevent+jwt"; // 修正iat为秒级时间戳,补充events字段 token.Payload["iat"] = new DateTimeOffset(utcNow).ToUnixTimeSeconds(); token.Payload["events"] = new Dictionary<string, object> { [eventTargetUrl] = new Dictionary<string, object>() }; return tokenHandler.WriteToken(token); }
注意事项
- 不要使用普通RS512算法签名,必须使用PS512,二者填充模式不同,签名结果不兼容
typ头必须严格设置为secevent+jwt,不能使用默认的JWT值,否则接收方会校验失败iat字段必须是UTC时区的秒级Unix时间戳,不要使用本地时间、不要传毫秒级时间戳events字段结构必须为「URL作为键、空对象作为值」的嵌套对象,不要写错格式- 生成后可以把令牌字符串做Base64解码,核对头、载荷字段是否和Java示例预期完全一致,再验证签名有效性即可。
内容的提问来源于stack exchange,提问作者Gray Paw
相关产品推荐
相关产品推荐

