跳到主要内容

iOS 推送用 p8 还是 p12?APNs 推送密钥与证书的区别和迁移

iOS 推送新项目用 .p8 认证密钥:它不会过期,一个密钥能给团队下所有 App 发推送;.p12 推送证书每个 App 一张、一年到期。本文讲两者的创建步骤、服务端配置、从 p12 迁到 p8 和 APNs 返回码排查表。

iOS 推送新项目用 .p8 认证密钥,不用 .p12 推送证书:.p8 不会过期,一个密钥能给团队下所有 App 发推送;.p12 每个 App 一张、一年到期,只在对接只支持证书的老系统时才需要。从 p12 换到 p8 只改服务端,App 不用发新版本。下文讲两者的创建步骤、服务端和推送平台配置、迁移步骤和 APNs 返回码排查表,Apple 规则截至 2026 年 10 月,都附了官方链接。

第一次接推送、还没跑通过链路的,先按给 iOS App 加推送通知从开通能力做到真机收到第一条,这篇专门讲凭据怎么选、怎么换。

iOS 推送的 p8 和 p12 有什么区别?

对比项 .p8 认证密钥(Token) .p12 推送证书(Certificate)
在哪创建 Certificates, Identifiers & Profiles 的 Keys 页 Identifiers 页 App ID 的 Push Notifications → Configure,或 Certificates 页
拿到的文件 AuthKey_<Key ID>.p8,只能下载一次 .cer,要和生成 CSR 时的私钥合成 .p12
有效期 不过期,可以吊销 一年,到期前要换新证书
覆盖范围 团队密钥(Team Scoped)覆盖团队所有 App,包括以后新建的;主题密钥(Topic Specific)只覆盖选中的 App 一张证书只绑定一个 App ID
环境 创建时选环境,Apple 建议开发和生产各用一个密钥 当前类型「Sandbox & Production」一张同时用于两个环境
服务端要做的事 每次请求带一个用 .p8 签名的 JWT(ES256),20 到 60 分钟换一次 建立 TLS 连接时出示证书,请求里不用带令牌
多个 App 一个密钥、一个连接池就够 每个 App 一张证书,各自管理连接
泄露后 先建新密钥,切换后再吊销旧的 吊销证书,APNs 拒绝用它建立的连接
需要的角色 账户持有人或管理员 账户持有人或管理员

表里的规则来自 Apple 的用令牌连接 APNs、用证书连接 APNs和帮助页用认证令牌与 APNs 通信。证书文档原话是 Provider certificates are valid for a year,过期后不能再发推送,见证书过期或被吊销会怎样。

关于密钥的环境

Apple 现在的文档写明,新建的团队密钥限定在 Sandbox 或 Production 其中一个环境,每个环境最多两个团队密钥。以前建的、两个环境都能用的密钥继续有效,但 Apple 建议改成按环境分开。主题密钥每个环境最多 200 个,每个最多关联 400 个 topic。

按环境分开后,开发环境(api.sandbox.push.apple.com)用开发密钥签 JWT,生产环境(api.push.apple.com)用生产密钥签。用错了,APNs 返回 403 BadEnvironmentKeyIdInToken。

怎么选

你的情况 选
新项目,自建推送服务 .p8 团队密钥,开发、生产各一个
一个团队有多个 App .p8 团队密钥,一个密钥覆盖全部
想让某个服务只能给指定 App 发 .p8 主题密钥,只勾选这几个 App
第三方推送平台两种都支持 .p8,省掉每年换证书
老系统或平台只支持证书 .p12,并在日历上记好到期日

怎么创建 .p8 认证密钥?

  1. 在 Certificates, Identifiers & Profiles 的 Keys 页点「+」,填一个名字,勾选 Apple Push Notification service (APNs)。
  2. 点旁边的 Configure,按页面上的选项选环境和密钥类型(Team Scoped 或 Topic Specific)。选 Topic Specific 的,再勾选要关联的 App。
  3. 确认后点 Download。.p8 文件只能下载一次,Download 按钮变灰说明已经下过,丢了只能吊销重建。
  4. 记下 Key ID(密钥详情页上,名字下方的 10 位字符)和 Team ID(开发者网站 Membership details 里的 10 位字符)。

步骤以 Apple 的创建私钥和获取密钥标识符为准。App ID 上还要勾选 Push Notifications 能力,做法见推送教程的开通能力一节。用 .p8 时 Push Notifications 旁边的 Configure 不用管。

.p8 等同于整个团队的推送权限。把它放在服务器的密钥管理里,不要提交到代码仓库,也不要打进 App。

创建 .p12 推送证书

证书要先有一份 CSR(证书签名请求),Apple 签发 .cer 后,再和 CSR 对应的私钥合成 .p12。在 Windows、Linux 上可以用 OpenSSL 完成,和申请开发证书的做法一样。

先生成私钥和 CSR。

1openssl req -new -newkey rsa:2048 -nodes -keyout push.key -out push.csr -subj "/emailAddress=you@example.com/CN=MyApp Push/C=CN"

然后到开发者后台申请证书。

  1. 在 Identifiers 页点开 App 的 App ID,勾选 Push Notifications,点 Configure。
  2. 在 Production SSL Certificate 下点 Create Certificate,上传 push.csr,点 Continue。
  3. 点 Download 下载 .cer 文件,下面假设叫 aps.cer。

最后合成 .p12,导出时设置的密码要告诉服务端或推送平台。

1openssl x509 -in aps.cer -inform DER -out aps.pem
2openssl pkcs12 -export -inkey push.key -in aps.pem -out push.p12

查看证书绑定的 App 和到期时间。

1openssl x509 -in aps.pem -noout -subject -enddate

subject 里能看到 Bundle ID,notAfter 是到期时间。步骤以 Apple 的用 TLS 证书与 APNs 通信为准,那一页写的是在 Mac 上用钥匙串导出,原理相同。

推送证书只给服务器用。在 Windows、Linux 上用 KXBuild 构建时,不要把它放进 keychain 证书目录,那里只放给 App 签名的开发证书和发布证书,见证书与签名。

服务端和第三方推送平台怎么配?

不管自建服务还是用极光、个推、Firebase 这类第三方平台,要准备的材料是同一套。第三方平台在它的后台上传即可,界面以平台文档为准。

用 .p8 时提供 用 .p12 时提供
.p8 文件 .p12 文件
Key ID 导出 .p12 时设的密码
Team ID 证书对应的环境(有的平台要求开发、生产分开上传)
Bundle ID(推送的 topic) Bundle ID(多数平台从证书里自动读出)
环境:开发或生产

配置时最容易错的是这三处。

  • Bundle ID 一字不差。请求头 apns-topic 就是 App 的 Bundle ID,和构建的不一致会返回 DeviceTokenNotForTopic。Ad Hoc 测试包改过 Bundle ID 的(例如 com.example.myapp.test),后台也要按测试包的填,见 iOS 多环境打包。
  • 环境和包对应。用 Development 描述文件签名的 Debug 包走开发环境;TestFlight、App Store、Ad Hoc 包走生产环境。两个环境的设备令牌不通用。
  • 设备令牌是最新的。App 每次启动都把令牌上报给后台,删除重装、换机后令牌可能变。

自建服务时再注意几点,均出自 Apple 的向 APNs 发送通知请求和令牌文档。

  • 用 HTTP/2 和 TLS 1.2 及以上连接,端口 443,防火墙只放行推送时可以改用 2197。
  • JWT 的 iat 超过一小时就会被拒,返回 ExpiredProviderToken。同一个连接上 20 分钟内换新 JWT 太频繁,返回 TooManyProviderTokenUpdates。做一个定时任务每 20 到 60 分钟重新签一次。
  • 一个连接只能对应一个团队。管理多个开发者账号的推送,要分开建连接池。

怎么从 p12 迁移到 p8?

迁移只改服务端,App 不用发新版本,设备令牌也不用重新收集。令牌由手机按 App 和环境生成,和服务端用密钥还是证书认证无关。

  1. 建密钥。按上文创建 .p8。建密钥不影响正在用的证书,两者可以同时有效。
  2. 先在开发环境验证。服务端或推送平台新增一套 .p8 配置,用 Debug 包的设备令牌发到开发环境,确认返回 200、手机收到。
  3. 切生产。生产环境改用 .p8,用 TestFlight 包的令牌验证一次,再观察一两天正式用户的发送成功率。
  4. 换用新连接。自建服务的,切换后关掉用证书建立的旧连接,用新连接发送。Apple 文档写明,连接在第一次推送时就和所用的团队、密钥绑定,之后换用无关的密钥会报错。
  5. 处理旧证书。确认没有任何服务还在用后,可以吊销证书,或者让它自然到期。吊销后 APNs 会拒绝所有用它建立的连接,先确认再动手。

用第三方平台的,在平台后台上传 .p8 并填好 Key ID、Team ID、Bundle ID,按平台文档的说明切换,再用测试设备各发一条确认。

iOS 推送失败按返回码怎么排查?

APNs 每个请求都会返回状态码,失败时响应体里有 {"reason":"..."}。下表的 reason 字符串和含义以 Apple 的处理 APNs 的响应为准,「常见原因」是对应到实际配置的排查方向。

状态码 reason 含义 常见原因与处理
400 BadDeviceToken 设备令牌无效 令牌和环境不匹配,Debug 包的令牌发到了生产地址或者反过来;令牌复制不完整
400 DeviceTokenNotForTopic 令牌和 topic 不对应 apns-topic 和 App 的 Bundle ID 不一致,常见于测试包改了 Bundle ID
400 TopicDisallowed 不允许向这个 topic 推送 核对 topic 是否属于这个团队、App ID 是否开了 Push Notifications、主题密钥是否勾选了这个 App
400 BadTopic、MissingTopic topic 无效或没填 请求头补上 apns-topic,值为 Bundle ID
400 InvalidPushType apns-push-type 无效 普通通知填 alert,静默推送填 background
403 InvalidProviderToken JWT 无效或签名验证不过 Key ID、Team ID 填错,.p8 不是这个团队的,签名不是 64 字节的原始格式
403 ExpiredProviderToken JWT 过期 JWT 超过一小时,或服务器时间不准,校准时间并定时重签
403 BadEnvironmentKeyIdInToken 密钥和环境不符 开发密钥发到了生产环境或者反过来
403 UnrelatedKeyIdInToken 和这个连接第一次推送用的密钥无关 换密钥后要新建连接
403 MissingProviderToken 没带证书也没带 JWT 请求头补上 authorization: bearer <JWT>
403 BadCertificate 证书无效 .p12 损坏、密码错、证书已过期或被吊销
403 BadCertificateEnvironment 证书和环境不符 证书和所连的地址(开发或生产)对不上,换成对应环境的证书或改连接地址
410 Unregistered 令牌对这个 topic 已失效 常见于 App 已从手机删除,停止向这个令牌发送,除非 App 再次上报同一个令牌
410 ExpiredToken 设备令牌已过期 等 App 下次启动上报新令牌
413 PayloadTooLarge payload 太大 普通推送不超过 4KB,大图片改成通知服务扩展下载
429 TooManyProviderTokenUpdates JWT 更新太频繁 一个 JWT 复用 20 到 60 分钟,不要每次请求都重签
429 TooManyRequests 对同一令牌发送太密 降低频率,延迟后再试

Apple 要求 BadDeviceToken、DeviceTokenNotForTopic、Forbidden、ExpiredToken、Unregistered、PayloadTooLarge 这几种不要原样重试,改好配置或换新令牌再发。5xx 错误可以在 15 分钟后带退避重试。太多 4xx 错误会让 APNs 降低你的发送速度甚至断开连接。

返回 200 却收不到、或拿不到令牌怎么办?

返回 200 只说明 APNs 收下了。手机端收不到、或者 App 根本拿不到设备令牌,多半是 App 签名里的 aps-environment 有问题。

现象 原因 处理
注册推送失败,no valid "aps-environment" entitlement string found for application 签名里没有 aps-environment App ID 勾选 Push Notifications,重新生成描述文件,工程的 entitlements 加上这个键
构建报描述文件不含 Push Notifications 描述文件是开通能力前生成的 编辑描述文件后直接保存,重新下载替换
上传 App Store Connect 报 ITMS-90078 代码注册了推送,签名里没有推送权限 见 ITMS-90078
Debug 收得到,TestFlight 收不到 后台还在用开发环境地址或开发令牌 生产包用生产环境地址和它上报的新令牌
推送证书时代能收,换 .p8 后收不到 密钥环境选错,或发送端还连着旧连接 看返回的 reason,对照上表

aps-environment 的值由描述文件决定。Apple 的 aps-environment 文档写明,开发描述文件对应 development(也叫 sandbox),发布描述文件和 TestFlight 对应 production。在 Windows、Linux 上用 KXBuild 构建时,entitlements 文件里写 development 即可,构建时按所用描述文件取值,配置方法见推送教程的 entitlements 一节。描述文件相关的构建报错见 iOS 签名报错与 ipa 安装失败。凭据、环境和签名三者都对上,iOS 推送的链路才算完整。

常见问题

APNs 的 .p8 密钥会过期吗?

不会。Apple 写明 APNs 签名密钥不会过期,但可以被吊销。要换密钥时先建新的,服务端切换完成后再吊销旧的。

一个 .p8 密钥能给多个 App 发推送吗?

能。团队密钥覆盖团队下所有 App,包括以后新建的,发送时用 apns-topic 指定发给哪个 App。只想覆盖部分 App 时,建主题密钥并勾选这几个 App。

推送证书 .p12 过期了会怎样?

服务器不能再用它发推送,App 本身照常运行,只是收不到通知。推送证书有效期一年,到期前换新证书,或者趁这次改用 .p8 密钥。

从 p12 换成 p8,需要重新发版或重新收集设备令牌吗?

都不需要。设备令牌由手机按 App 和环境生成,和服务端用哪种凭据无关,只要在服务端或推送平台把凭据换掉。

第三方推送平台应该上传 p8 还是 p12?

平台支持 .p8 就上传 .p8,同时填 Key ID、Team ID 和 Bundle ID,以后不用每年换证书。平台只支持证书时上传 .p12 和导出密码,并注意按开发、生产环境分别配置。