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

获取Apple公钥时遭遇401 Unauthorized错误求助

问题:获取Apple公钥时持续返回401 Unauthorized错误

执行代码后得到以下错误输出:

In [14]: print(f"Error fetching public keys: {response.status_code} {response.text}")
Error fetching public keys: 401 Unauthenticated

已确认以下信息无误:

  • Key ID、Issuer ID和私钥文件均正确
  • 私钥拥有管理员权限
  • 服务器时间已正确设置为UTC

尝试了两种生成Apple Developer Token的实现方式,且两种方式生成的Token在jwt.io验证均显示正确,但认证仍失败,寻求排查建议。


第一种手动实现代码

def generate_apple_developer_token():
    # Load the private key in PEM format
    with open(PRIVATE_KEY_FILE, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend()
        )

    # JWT header
    headers = {
        "alg": "ES256",  # Algorithm: Elliptic Curve
        "kid": KEY_ID,   # Key ID from Apple Developer
        "typ": "JWT"     # Type: JWT
    }

    # JWT payload
    payload = {
        "iss": ISSUER_ID,  # Issuer ID from Apple Developer
        "iat": int(datetime.utcnow().timestamp()),  # Issued at time
        "exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()),  # Expiration (max 10 minutes)
        "aud": "appstoreconnect-v1",  # Audience
        
    }

    # Encode the header and payload as base64
    header_base64 = base64.urlsafe_b64encode(json.dumps(headers).encode()).decode().rstrip("=")
    payload_base64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")

    # Concatenate header and payload
    message = f"{header_base64}.{payload_base64}".encode()

    # Sign the message using ECDSA with SHA256
    signature = private_key.sign(
        message,
        ec.ECDSA(hashes.SHA256())
    )

    # Convert the DER-encoded signature to raw format (r and s concatenated)
    der_to_raw_ecdsa_format = lambda der: der[4:36] + der[-32:]

    # Convert the signature to raw format (64 bytes)
    signature_64 = der_to_raw_ecdsa_format(signature)

    # Base64 URL-encode the signature
    signature_base64 = base64.urlsafe_b64encode(signature_64).decode().rstrip("=")

    # Concatenate header, payload, and signature to form the JWT
    jwt_token = f"{header_base64}.{payload_base64}.{signature_base64}"

    return jwt_token

def get_apple_public_keys():
    try:
        # Generate a fresh JWT
        developer_token = generate_apple_developer_token()

        # Set up headers with the authorization token
        headers = {
            "Authorization": f"Bearer {developer_token}"
        }

        # Fetch the public keys from Apple
        response = requests.get('https://api.storekit.itunes.apple.com/in-app-purchase/publicKeys', headers=headers)
         # Log the response if it's not successful
        if response.status_code != 200:
            print(f"Error fetching public keys: {response.status_code} {response.text}")
        
        response.raise_for_status()  # Raises an exception for 4xx/5xx errors

        # Parse and return the public keys
        response_data = response.json()
        keys = response_data.get('keys')
        if not keys:
            print("No 'keys' found in the response from Apple.")
            return []
        return keys
    except requests.exceptions.RequestException as e:
        print(f"Error fetching Apple's public keys: {e}")
        return []

第二种使用jwt库的实现代码

def generate_apple_developer_token():
    with open(PRIVATE_KEY_FILE, 'r') as f:
        private_key = f.read().strip()
        print('this is the key', private_key)
    # JWT header
    headers = {
        "alg": "ES256",  # Apple uses ES256 (Elliptic Curve)
        "kid": KEY_ID,
        "typ": "JWT"
    }

    # JWT payload
    payload = {
        "iss": ISSUER_ID,
        "iat": int(datetime.utcnow().timestamp()),  # Issued at time
        "exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()),  # Expiration (max 10 minutes)
        "aud": "appstoreconnect-v1",
    
    }

    # Generate and return the JWT
    return jwt.encode(payload, private_key, algorithm="ES256", headers=headers)

排查建议

  • 核对接口的认证要求:StoreKit的公钥接口(https://api.storekit.itunes.apple.com/in-app-purchase/publicKeys)部分场景下无需携带开发者Token,尝试移除Authorization请求头后重新请求,看是否能正常返回数据。
  • 检查Token的受众(aud字段):App Store Connect API的aud为appstoreconnect-v1,但StoreKit相关接口可能需要不同的受众值,确认当前调用接口对应的正确aud参数。
  • 验证私钥格式完整性:确保私钥文件包含完整的-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----标记,无多余空格、换行或字符。
  • 确认时间戳准确性:打印生成的iat和exp时间戳,验证是否与当前UTC时间一致,避免因代码时区处理隐性问题导致时间偏差。
  • 检查私钥权限范围:在Apple Developer后台确认私钥关联的API权限包含“In-App Purchase”相关权限,仅管理员权限不足以覆盖所有API操作。
  • 排查网络环境:确认服务器无代理、防火墙干扰,请求头未被篡改,可通过抓包工具验证发送的Authorization头是否完整。
  • 重新生成私钥:若以上均无问题,尝试在Apple Developer后台生成新的Key ID和私钥,替换后重新测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 03:47:32