博客 · · 指南

App Store Connect API 密钥:Issuer ID、Key ID 和 .p8

命令行上传、CI、脚本、拉销售报表、让编程 Agent 操作 App Store Connect,第一步都是同一件事:一把 App Store Connect API 密钥。这篇讲清楚怎么创建、三样东西各填在哪、该给它什么职能。

三样东西:Issuer ID、Key ID 和 .p8

API 密钥分两半:公钥留在 Apple,私钥由你下载,文件名是 AuthKey_<Key ID>.p8。每次请求 API 时,用私钥签一个短时有效的令牌(JWT)。用到的三样东西必须是同一套:

名称 样子 在哪里
Issuer ID 57246542-96fe-1a63-e053-0824d011072a “用户和访问” ›“集成”页面顶部,旁边有“拷贝”
Key ID 2X9R4HXF34(10 位) 密钥所在的那一行
私钥 AuthKey_2X9R4HXF34.p8 你下载的文件,Apple 不留副本

这种密钥只能用于 App Store Connect API。同在“集成”页面里的“App 内购买项目”密钥是另一类,给 App Store Server API 和订阅优惠用,两者不能混用。

团队密钥还是个人密钥

  • 团队密钥:能访问账户里的所有 App,权限由你给密钥选的职能决定,不能只限定部分 App。只有账户持有人和“管理”职能的成员能创建。
  • 个人密钥:权限和创建它的那个人一样,每人同一时间只能有一把。它不能用 Provisioning 相关接口,不能拉销售和财务报告,也不能用于 notarytool。

要拉销售或财务报表,或者希望有人离开团队后密钥照常能用,就选团队密钥。

第一次用之前:请求访问权限

第一次使用时,要由账户持有人在“用户和访问” ›“集成”里点“请求访问权限”,勾选同意条款后提交。Apple 会逐个审核。批准之后,账户持有人和“管理”职能的成员就能创建团队密钥了。

创建团队密钥

  1. 打开 App Store Connect 的“用户和访问”,点“集成”,默认就在 App Store Connect API 下。
  2. 点“团队密钥”。
  3. 点“生成 API 密钥”;已经有密钥的话,点添加按钮(+)。
  4. 填一个名称,只给自己看,比如写上用它的工具或机器。
  5. 在“访问”下选择密钥的职能,见下一节。
  6. 点“生成”,新密钥会带着 Key ID 出现在列表里。
  7. 点“下载 API 密钥”,把 .p8 收好。下载过一次,链接就不再出现。

密钥生成后,名称和职能都不能改。要改,只能撤销后重新生成。

什么工作选什么职能

密钥的职能(也常叫“角色”)和团队成员的职能是同一套,够用就好。下表依据 Apple 的职能权限,只算“完全访问”,不算“只读访问”:

要做的事 需要的职能
上传构建 App 管理或开发者(均需有“证书、标识符和描述文件”访问权限)
提交审核、发布版本、分阶段发布 App 管理
创建、编辑和提交 App 内购买项目 App 管理
TestFlight 构建和外部测试组 App 管理
TestFlight 内部测试组 App 管理、开发者或营销
销售和趋势报告 财务,或有报告访问权限的销售
付款和财务报告 财务
查看用户评论 App 管理、开发者、营销或客户支持
回复用户评论 客户支持

大多数情况套这两种就够:负责上传、分发给外部测试员和提审的 CI 机器给“App 管理”;只读销售和付款的看板或脚本给“财务”。两件事都要做的工具,两个职能都得有。“管理”职能什么都能做,所以更不该放在构建服务器上。

私钥怎么保管

  • 放进密码管理器或钥匙串,别放进代码仓库。Apple 的原话是:不要分享密钥,不要存进代码库,也不要放进客户端代码。
  • 编程 Agent 能读到项目里的所有文件,密钥文件请放在 Agent 工作目录之外。
  • 命令行工具会在固定位置找密钥。比如 altool 会在 ~/.appstoreconnect/private_keys/ 等目录里找 AuthKey_<Key ID>.p8。

签一个 JWT

每个请求都带 Authorization: Bearer <token>,令牌是用 ES256 签名的 JWT:

  • 头部:alg 为 ES256,kid 为 Key ID,typ 为 JWT。
  • 载荷:iss 为 Issuer ID,iat 为当前时间,exp 最多比 iat 晚 20 分钟,aud 为 appstoreconnect-v1。可选的 scope 能把令牌限定在你列出的请求上。
  • 个人密钥不用 iss,改用 sub,值为 user。

用 Apple 自带的 CryptoKit,十几行就够。存成 jwt.swift,和密钥文件放在同一个文件夹,运行 swift jwt.swift:

import CryptoKit
import Foundation

let keyID = "2X9R4HXF34"                              // Key ID
let issuerID = "57246542-96fe-1a63-e053-0824d011072a" // Issuer ID
let pem = try String(contentsOfFile: "AuthKey_\(keyID).p8", encoding: .utf8)

func base64URL(_ data: Data) -> String {
    data.base64EncodedString()
        .replacingOccurrences(of: "+", with: "-")
        .replacingOccurrences(of: "/", with: "_")
        .replacingOccurrences(of: "=", with: "")
}

let now = Int(Date().timeIntervalSince1970)
let header = #"{"alg":"ES256","kid":"\#(keyID)","typ":"JWT"}"#
let payload = #"{"iss":"\#(issuerID)","iat":\#(now),"exp":\#(now + 1200),"aud":"appstoreconnect-v1"}"#
let input = base64URL(Data(header.utf8)) + "." + base64URL(Data(payload.utf8))
let key = try P256.Signing.PrivateKey(pemRepresentation: pem)
let signature = try key.signature(for: Data(input.utf8))
print(input + "." + base64URL(signature.rawRepresentation))

试一下:

curl -H "Authorization: Bearer $(swift jwt.swift)" https://api.appstoreconnect.apple.com/v1/apps

签名要用 64 字节的原始格式(rawRepresentation),不是 DER。令牌不用每次请求都重签,过期前可以一直复用;只发一次请求的话,Apple 建议有效期短到两分钟就行。

报错怎么查

  • 401 Unauthorized:Apple 的状态码表里没有列出它,实际使用中它表示令牌没被接受。按 Apple 的令牌规则逐项核对:Key ID、Issuer ID 和 .p8 属于同一把密钥;exp 最多比 iat 晚 20 分钟(只有部分限定范围的只读请求允许更长);本机时间准确;aud 为 appstoreconnect-v1;个人密钥的令牌用 sub 而不是 iss。
  • 403 Forbidden:Apple 文档列出的原因是密钥已撤销、令牌格式不对,或者这个操作不在密钥职能的权限内。典型例子:只有“App 管理”职能的密钥去拉销售报告。
  • 429 Too Many Requests:限额按每把密钥、滚动一小时计算。每个响应都带 X-Rate-Limit 头,写着上限和剩余次数。

撤销和更换

“用户和访问” ›“集成” ›“团队密钥”,点“编辑”,选中密钥后点“撤销密钥”。撤销后无法恢复,已撤销的密钥会在列表里保留 30 天。想无缝更换:先建新密钥,把工具都换过去,再撤销旧的。

在 SwiftSales 里用这把密钥

SwiftSales 的两部分都用你的团队密钥:仪表盘背后的销售和财务报表,以及 Agent 通过它的 App Store Connect MCP 服务器替你做的 App Store Connect 工作。

用哪把密钥、选哪个职能

  • 团队密钥。 销售和财务报表只能用团队密钥读取,个人密钥不行。
  • 推荐“财务”加“App 管理”。 “财务”能读取财务报表,财月显示的是 Apple 实际结算的金额;“App 管理”让 Agent 能上传构建、安排 TestFlight、提审、发布和创建 App 内购买项目。
  • 只选“财务”,仪表盘就够用了。以后想让 Agent 帮你发布,再加一把发布密钥:另一把选了“App 管理”的团队密钥,在 Mac 上的 SwiftSales 里到“设置 › 团队”添加。发布密钥只用于发布相关的工作。
  • **“销售”**职能加上报告访问权限,也能读取销售数据,这时财月金额根据每日报表估算。

密钥做不了某件事时,SwiftSales 会告诉你该加哪个职能。在 App 里怎么操作,见创建团队密钥。

密钥存在哪里

.p8 文件只需导入一次,再填上 Issuer ID 和 Vendor 编号;文件保留原名时,SwiftSales 会自动填好 Key ID。密钥保存在你的钥匙串里;打开 iCloud 同步后(永久高级版的功能,免费试用期间也可以使用),你的其他 iPhone、iPad 和 Mac 也能使用;除此之外,它哪儿也不去。SwiftSales 在你的设备上签名每个请求、直接和 Apple 通信,所以密钥不用放进任何项目文件夹,Agent 也看不到它。

SwiftSales 设置中的团队 Harbor Lane Studio:Key ID、Issuer ID,以及“可读取销售、财务、评论,可以提审”SwiftSales 设置中的团队 Harbor Lane Studio:Key ID、Issuer ID,以及“可读取销售、财务、评论,可以提审”SwiftSales 设置中的团队 Harbor Lane Studio:Key ID、Issuer ID,以及“可读取销售、财务、评论,可以提审”SwiftSales 设置中的团队 Harbor Lane Studio:Key ID、Issuer ID,以及“可读取销售、财务、评论,可以提审”
SwiftSales 里的团队和它的密钥(演示数据)

连上之后能看到什么

SwiftSales 直接从 Apple 下载你的报表。总览把每天关心的问题各放在一张卡片上:任意时间段的收入、下载量、新付费用户、活跃订阅和 MRR,以及 Apple 下一次什么时候打款。主屏幕和锁定屏幕小组件、Mac 菜单栏和每天一条通知,显示最新一天的数据。完整介绍见《SwiftSales 来了》。

MacBook Air 上的 SwiftSales:总览里的收入、下载量、新付费用户、订阅和下次打款MacBook Air 上的 SwiftSales:总览里的收入、下载量、新付费用户、订阅和下次打款MacBook Air 上的 SwiftSales:总览里的收入、下载量、新付费用户、订阅和下次打款MacBook Air 上的 SwiftSales:总览里的收入、下载量、新付费用户、订阅和下次打款
Mac 上的总览(演示数据)

Agent 能用它做什么

在 Mac 上的 SwiftSales 里打开 Agent 接入,Claude Code、Codex、Cursor、Claude 或 Xcode 就能通过 SwiftSales 使用同一把密钥:回答关于销售和评论的问题,上传构建,安排 TestFlight,填写商店页面,提交审核和发布。每个 Agent 默认是“只读”,级别和能看哪些 App 由你决定,提交审核和发布都要你在 Mac 上批准。用 Claude Code 上架 App Store 按步骤走完一次发布,每一步都有手动和用 SwiftSales 两种做法。

SwiftSales让你的 Agent 帮你发布 App。
  • 让 Agent 把 App 发上去Claude Code、Codex、Cursor、Claude 或 Xcode 通过你 Mac 上的 SwiftSales 上传构建、安排 TestFlight、填写商店页面并提交审核。
  • 销售数据一眼看清iPhone、iPad 和 Mac 上的仪表盘,主屏幕和锁定屏幕小组件,Mac 菜单栏,每天一条通知。
  • 你的密钥,你来批准团队密钥保存在你的钥匙串里,Agent 看不到。提交审核和发布都要你在 Mac 上批准。
  • 直接来自 Apple报表从 Apple 直接到你的设备和你自己的 iCloud,我们的服务器看不到它们。你连接的 Agent 只能读取你允许的数据,并交给它所用的 AI 服务。

免费下载 · 7 天免费试用 · 永久高级版 US$9.99

一次购买,无需订阅。支持家人共享。