.NET中验证Apple IAP回调JWS签名失败求助
Apple IAP 回调JWS验证问题(C#)
问题背景
开发iOS应用集成Apple内购时,遇到服务器回调JWS验证的技术卡点。查阅资料多为Java/PHP实现,尝试用C#编写时因API差异受阻,目前无法完成JWS有效性验证。
需求
用户购买成功后,Apple服务器会发送包含JWS的回调JSON,需验证该JWS的真实性。
当前进度
回调信息初步验证
- 回调JSON中的
payload为JWS,已通过JWT调试工具确认有效。
- 回调JSON中的
证书链验证通过
- 用
X509Chain验证JWS头x5c字段的证书链,代码如下,验证结果为true:
// 手动下载的AppleRootCA-G3.cer var AppleRootCA = new X509Certificate2("AppleRootCA-G3.cer"); var handler = new JwtSecurityTokenHandler(); var jsonToken = handler.ReadToken(dto.signedPayload) as JwtSecurityToken; // dto为Apple发送的回调JSON,signedPayload是JWS var itor = jsonToken.Header["x5c"] as IReadOnlyList<object>; // certificate2指纹与AppleRootCA一致 X509Certificate2 certificate2 = new X509Certificate2(Convert.FromBase64String((string)itor[2])); X509Certificate2 certificate1 = new X509Certificate2(Convert.FromBase64String((string)itor[1])); X509Certificate2 certificate0 = new X509Certificate2(Convert.FromBase64String((string)itor[0])); // 已提前信任AppleRootCA-G3.cer,无需重复添加 // X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser); // store.Open(OpenFlags.ReadWrite); // store.Add(AppleRootCA); using var chain = new X509Chain(); // chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.ExtraStore.Add(certificate2); chain.ChainPolicy.ExtraStore.Add(certificate1); chain.ChainPolicy.ExtraStore.Add(certificate0); var isValid = chain.Build(AppleRootCA); Console.WriteLine(isValid); // 输出true- 用
令牌验证失败
- 尝试以下代码验证令牌,持续报错:
var validationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new X509SecurityKey(AppleRootCA), ValidateIssuer = false, ValidateAudience = false, // RequireExpirationTime = true, ValidateLifetime = true, // TryAllIssuerSigningKeys = true }; try { handler.ValidateToken(dto.signedPayload, validationParameters, out var st); // await handler.ValidateTokenAsync(jsonToken, validationParameters); Console.WriteLine("success"); // return true; } catch (Exception ex) { Console.WriteLine($"false: {ex.Message}"); // return false; }- 错误信息:
IDX10517: Signature validation failed. The token's kid is missing. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'B52CB02FD567E0359FE8FA4D4C41037970FE01B0', InternalId: 'tSywL9Vn4DWf6PpNTEEDeXD-AbA'. , KeyId: B52CB02FD567E0359FE8FA4D4C41037970FE01B0 '. Number of keys in TokenValidationParameters: '1'. Number of keys in Configuration: '0'. Exceptions caught: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10503 for details.- 尝试将
IssuerSigningKey替换为certificate0、certificate1、certificate2,错误依旧,无法完成JWS有效性验证。
补充:AppleRootCA-G3.cer来自苹果官方渠道下载。
解决方案
问题核心在于未使用JWS签名证书(x5c第一个证书)验证签名,且未正确关联KeyId,以下是修正后的完整验证代码:
var AppleRootCA = new X509Certificate2("AppleRootCA-G3.cer"); var handler = new JwtSecurityTokenHandler(); var jsonToken = handler.ReadToken(dto.signedPayload) as JwtSecurityToken; var x5c = jsonToken.Header["x5c"] as IReadOnlyList<object>; // 提取签名证书(x5c第一个元素为签名用证书) var signingCert = new X509Certificate2(Convert.FromBase64String((string)x5c[0])); // 提取中间证书与根证书 var intermediateCert = new X509Certificate2(Convert.FromBase64String((string)x5c[1])); var rootCert = new X509Certificate2(Convert.FromBase64String((string)x5c[2])); // 配置验证参数 var validationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, // 使用签名证书公钥,关联JWT头中的kid IssuerSigningKey = new X509SecurityKey(signingCert) { KeyId = jsonToken.Header.Kid }, ValidateIssuer = true, ValidIssuer = "https://appleid.apple.com", // Apple IAP固定签发者 ValidateAudience = true, ValidAudience = "你的App Bundle ID", // 替换为应用实际Bundle ID ValidateLifetime = true, // 自定义证书链验证逻辑 CertificateValidator = (cert, chain, policy) => { chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.ExtraStore.Add(rootCert); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // 生产环境建议开启吊销检查 return chain.Build(cert); } }; try { handler.ValidateToken(dto.signedPayload, validationParameters, out var validatedToken); Console.WriteLine("JWS验证成功"); // 可从validatedToken中提取交易信息 var payload = validatedToken as JwtSecurityToken; var transactionId = payload.Claims.First(c => c.Type == "transaction_id").Value; } catch (Exception ex) { Console.WriteLine($"验证失败:{ex.Message}"); }
关键说明
- 签名证书必须为x5c第一个元素:Apple使用该证书签名JWS,根证书仅用于证书链校验,不能直接用于签名验证。
- KeyId必须匹配:JWT头的
kid需与签名证书的KeyId一致,否则会触发kid缺失错误。 - 签发者与受众不可省略:固定签发者为
https://appleid.apple.com,受众为应用Bundle ID,可有效防止伪造令牌。 - 证书链验证需包含中间证书:确保签名证书由Apple信任的CA签发,避免无效证书绕过验证。
内容的提问来源于stack exchange,提问作者BoysheO
相关产品推荐
相关产品推荐

