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

.NET中验证Apple IAP回调JWS签名失败求助

Apple IAP 回调JWS验证问题(C#)

问题背景

开发iOS应用集成Apple内购时,遇到服务器回调JWS验证的技术卡点。查阅资料多为Java/PHP实现,尝试用C#编写时因API差异受阻,目前无法完成JWS有效性验证。

需求

用户购买成功后,Apple服务器会发送包含JWS的回调JSON,需验证该JWS的真实性。

当前进度

  1. 回调信息初步验证

    • 回调JSON中的payload为JWS,已通过JWT调试工具确认有效。
  2. 证书链验证通过

    • 用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
    
  3. 令牌验证失败

    • 尝试以下代码验证令牌,持续报错:
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:08:13