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

求助:用Java解码苹果App Store API返回的JWSTransaction遇报错

解码苹果App Store API返回的JWSTransaction Java代码修正

错误原因

你犯了两个核心错误:

  • 密钥类型错误:验证苹果签名的JWT时,不能用你自己的P8私钥。苹果的JWSTransaction是苹果用他们的RSA私钥签名的,我们需要用苹果官方发布的公钥来验证签名,而不是你用来向苹果API发起请求的私钥。
  • 算法不匹配:苹果的JWSTransaction使用的签名算法是RS256,但你代码里用了EC(椭圆曲线)密钥,这就导致了算法不兼容的错误。

正确解决方案

要解码并验证苹果的JWSTransaction,步骤如下:

  • 从苹果官方的JWT公钥端点(地址为https://appleid.apple.com/auth/keys)获取公钥集合
  • 根据JWT头部的kid字段,匹配对应的公钥
  • 使用该公钥验证JWT签名并解析内容

以下是基于JJWT库的可行代码:

依赖(Maven)

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

解码验证代码

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.List;
import java.util.Map;

public class AppleJwtDecoder {
    private static final String APPLE_PUBLIC_KEYS_URL = "https://appleid.apple.com/auth/keys";
    private List<Map<String, Object>> applePublicKeys;

    public AppleJwtDecoder() {
        // 初始化时获取苹果公钥,实际项目建议缓存(比如每24小时刷新一次),避免重复请求
        try {
            String keysJson = new String(
                    new URL(APPLE_PUBLIC_KEYS_URL).openStream().readAllBytes(),
                    StandardCharsets.UTF_8
            );
            applePublicKeys = (List<Map<String, Object>>) Jwts.parser().parse(keysJson).getBody().get("keys");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void decodeTransactionJwt(String transactionJwt) {
        try {
            // 先解析JWT头部获取kid字段
            JwtHeader header = Jwts.parser().parseClaimsJws(transactionJwt).getHeader();
            String kid = header.getKeyId();

            // 根据kid匹配对应的苹果公钥
            PublicKey publicKey = findPublicKeyByKid(kid);
            if (publicKey == null) {
                System.out.println("未找到匹配的苹果公钥");
                return;
            }

            // 验证签名并解析JWT内容
            Jws<Claims> jws = Jwts.parserBuilder()
                    .setSigningKey(publicKey)
                    .build()
                    .parseClaimsJws(transactionJwt);

            // 输出解析后的交易信息,可根据需求获取字段
            Claims claims = jws.getBody();
            System.out.println("解析成功:");
            System.out.println("transactionId: " + claims.get("transactionId"));
            System.out.println("productId: " + claims.get("productId"));
            System.out.println("purchaseDate: " + claims.get("purchaseDate"));
        } catch (JwtException e) {
            System.out.println("JWT验证失败:" + e.getMessage());
        } catch (Exception e) {
            System.out.println("解码出错:" + e.getMessage());
        }
    }

    private PublicKey findPublicKeyByKid(String kid) {
        for (Map<String, Object> keyMap : applePublicKeys) {
            if (kid.equals(keyMap.get("kid"))) {
                // 从公钥参数生成RSA公钥
                String modulus = (String) keyMap.get("n");
                String exponent = (String) keyMap.get("e");
                return Keys.createRsaPublicKeyFromModulusAndExponent(
                        Keys.base64UrlDecode(modulus),
                        Keys.base64UrlDecode(exponent)
                );
            }
        }
        return null;
    }

    // 测试入口
    public static void main(String[] args) {
        AppleJwtDecoder decoder = new AppleJwtDecoder();
        String transactionJwt = "xxx.yyy.zzz"; // 替换为你的JWSTransaction字符串
        decoder.decodeTransactionJwt(transactionJwt);
    }
}

关键注意点

  • 公钥缓存:苹果的公钥不会频繁变更,实际项目中建议缓存这些公钥,避免每次解码都请求苹果的公钥端点,提升性能并减少网络依赖。
  • 字段扩展:JWSTransaction包含很多交易相关字段,可根据业务需求从Claims对象中获取对应值。
  • 异常处理:实际生产环境中需完善异常捕获逻辑,比如网络请求失败、公钥解析失败等场景。

内容的提问来源于stack exchange,提问作者Mike Dee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 21:14:53