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

如何在外部服务器验证GameKit LocalPlayer身份签名及排查失败问题

问题:GameKit服务端身份验证签名验证失败

我正尝试按照苹果官方文档实现iOS GameKit客户端已认证用户的服务端身份验证。该API从客户端接收Player ID、timestamp和salt,同时返回公钥URL。我尝试从该URL获取证书提取公钥时遇到编码异常,所有解码格式都失败;但读取单独下载到本地的同一份证书却能正常工作,因此目前采用读取本地文件的方式获取证书。

我编写了如下验证流程代码,但签名验证步骤始终失败,抛出cryptography.exceptions.InvalidSignature异常,请问问题出在哪里?我找不到最新的解决方案或代码示例,查阅过10年前的相关帖子但方案已过时,自认为代码是其更新版本。

import struct
import base64
import hashlib
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography import x509
from cryptography.hazmat.primitives import hashes


# GameKit public key URL and other params
public_key_url = "https://static.gc.apple.com/public-key/gc-prod-9.cer"
player_id = <GameKit_TeamPlayerID>
bundle_id = <App_Bundle_ID>
timestamp = 1683019415788
salt = "OnSojw=="

# client-generated signature and payload
client_signature = <GameKit_Client_Signature>

client_payload =player_id.encode('UTF-8') + bundle_id.encode('UTF-8') + struct.pack('>Q',timestamp) + base64.b64decode(salt)

# Download and decode the GameKit public key
# Or read from local cer file
with open("gc-prod-9.cer", "rb") as f:
    cert_data = f.read()

# Parse the certificate data
cert = x509.load_der_x509_certificate(cert_data)
# Extract the public key from the certificate
public_key = cert.public_key()

# Hash the client payload with SHA-256
payload_hash = hashlib.sha256(client_payload).digest()

# Verify the client signature using the GameKit public key
signature_bytes = base64.b64decode(client_signature)

try:
    public_key.verify(
        signature_bytes,
        payload_hash,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    print("Client signature verified successfully")
except Exception as e:
    print("Client signature verification failed")
    print(str(e))

错误信息:

in _rsa_sig_verify
raise InvalidSignature
cryptography.exceptions.InvalidSignature


问题排查与解决方案

1. 核心错误:重复哈希Payload

你的代码中对Payload做了两次SHA-256哈希,这是导致验证失败的关键:
public_key.verify方法会自动对传入的原始Payload进行哈希计算,不需要提前手动哈希。你当前传入的是哈希后的payload_hash,相当于让验证逻辑对哈希结果再做一次哈希,最终和客户端签名的哈希值完全不匹配。

修改后的验证代码应直接传入原始Payload:

try:
    public_key.verify(
        signature_bytes,
        client_payload,  # 传入原始Payload,而非提前哈希的结果
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    print("Client signature verified successfully")
except Exception as e:
    print("Client signature verification failed")
    print(str(e))

2. Payload构造细节校验

  • Player ID类型:确认使用的是Team Player ID,而非客户端返回的普通Player ID,两者格式不同,不能混用
  • Timestamp单位:客户端返回的timestamp是毫秒级,struct.pack('>Q', timestamp)要求64位无符号整数,确认数值未超出范围;部分旧版本API使用秒级timestamp,可尝试转换为秒级测试
  • Bundle ID一致性:必须和客户端的Bundle ID完全一致,包括大小写、后缀,不能有任何拼写错误

3. 证书加载格式验证

苹果返回的.cer文件通常是DER格式,但如果下载时被浏览器自动转换为PEM格式(带有-----BEGIN CERTIFICATE-----头),用x509.load_der_x509_certificate会解析失败。可尝试替换为PEM加载方法测试:

cert = x509.load_pem_x509_certificate(cert_data)

4. Signature解码预处理

确认客户端传递的client_signature是标准Base64编码,无额外换行或空格,否则会导致解码错误:

client_signature = client_signature.strip().replace('\n', '')
signature_bytes = base64.b64decode(client_signature)

5. 可选:证书链验证

为避免伪造证书攻击,建议添加苹果根证书的链验证(需提前下载苹果官方根证书):

from cryptography.hazmat.primitives import serialization

# 加载苹果根证书
with open("AppleRootCA-G3.cer", "rb") as f:
    root_cert_data = f.read()
root_cert = x509.load_der_x509_certificate(root_cert_data)

# 验证当前证书由苹果根证书签发
cert.verify_directly_issued_by(root_cert)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 06:53:09