fastlane pem 完全指南:自动生成与续期 iOS 推送证书(.pem/.cer/.p12)
iOS 推送通知依赖 Apple 签名的推送证书,手工在 Developer Portal 创建证书、导出 .pem 文件的过程繁琐且容易过期遗忘。fastlane 内置的 pem 工具(对应 action 为 get_push_certificate)可以一条命令完成签名请求生成、证书申请、证书下载以及 .pem 文件产出的全流程,并在现有证书仍在有效期内时自动跳过重复创建。读完本文,你将掌握 pem 的完整用法、全部参数与环境变量、空密码 p12 文件的校验技巧,以及其底层如何通过 Spaceship 与 Apple Developer Portal 交互的实现原理。
功能概览
pem 的核心功能只有一件:为你的推送服务器生成 .pem 文件。运行 fastlane pem 时,它会依次完成:
- 创建新的证书签名请求(CSR);
- 在 Apple Developer Portal 上创建新的推送证书;
- 下载该证书;
- 在当前工作目录生成新的
.pem文件,供你上传到推送服务器。
需要注意两点边界:
pem不负责把文件上传到你的服务器——它只生成本地文件,上传逻辑需要自己实现(get_push_certificateaction 提供了new_profile回调,见下文);pem永远不会吊销你现有的证书,也无法下载已存在的推送证书,因为私钥只存在于当初创建它的那台机器上。如果现有证书仍活跃,pem只会提示并直接返回。
关于命名:.pem 文件中实际上拼接了 证书(.cer 内容)+ 私钥;此外 pem 还可以按配置额外输出 .p12 与 .pkey 文件。
基本用法
最简单的调用只有这一条命令:
fastlane pem
如果你的 Apple ID 或 App Identifier 已在 Appfile 中配置,甚至无需任何参数。也可以显式传参:
fastlane pem -a com.krausefx.app -u your@apple-id.com
按证书类型区分的常用命令:
# 默认:生产(Production)推送证书
fastlane pem
# 开发环境(Development)推送证书
fastlane pem --development
# Website Push 证书(Web Push 服务使用)
fastlane pem --website_push
# VoIP Services 证书(PushKit 所需类型)
fastlane pem --voip_push
强制创建新证书:当现有推送证书有效期还剩 30 天以上时,pem 不会创建新证书;如需强制新建,加 --force:
fastlane pem --force
其他常用操作:
# 为 p12 文件设置密码
fastlane pem -p "MyPass"
# 指定输出 .pem 文件名
fastlane pem -o my.pem
# 查看所有可用选项
fastlane action pem
完整参数与选项
pem 的选项定义在 pem/lib/pem/options.rb 中,fastlane action pem 展示的选项即来源于此。结合源码,完整参数如下(均支持对应环境变量):
| 参数 | 短选项 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|---|
platform |
-m |
PEM_PLATFORM |
ios |
证书平台,仅支持 ios 或 macos(源码中有 verify_block 校验,传其他值会直接报错) |
development |
— | PEM_DEVELOPMENT |
false |
续期开发推送证书而非生产证书 |
website_push |
— | PEM_WEBSITE_PUSH |
false |
创建 Website Push 证书(与 development 互斥) |
voip_push |
— | PEM_VOIP_PUSH |
false |
创建 VoIP Services 证书(与 development、website_push 互斥) |
generate_p12 |
— | PEM_GENERATE_P12_FILE |
true |
是否额外生成 .p12 文件(默认开启,可关闭) |
active_days_limit |
— | PEM_ACTIVE_DAYS_LIMIT |
30 |
现有证书有效期短于此天数时才重新生成;必须为正整数 |
force |
— | PEM_FORCE |
false |
即使现有证书仍活跃超过阈值也强制创建新证书 |
save_private_key |
-s |
PEM_SAVE_PRIVATEKEY |
true |
是否保存 RSA 私钥(.pkey 文件) |
app_identifier |
-a |
PEM_APP_IDENTIFIER |
取自 Appfile 的 app_identifier |
应用的 Bundle ID |
username |
-u |
PEM_USERNAME |
取自 Appfile 的 apple_dev_portal_id / apple_id |
Apple ID 用户名 |
team_id |
-b |
PEM_TEAM_ID |
取自 Appfile 的 team_id |
属于多个团队时的团队 ID(可选) |
team_name |
-l |
PEM_TEAM_NAME |
取自 Appfile 的 team_name |
属于多个团队时的团队名称(可选) |
p12_password |
-p |
PEM_P12_PASSWORD |
空 | p12 文件的密码(标记为敏感参数,摘要输出时会被隐藏) |
pem_name |
-o |
PEM_FILE_NAME |
无 | 指定生成的 .pem 文件名(可选) |
output_path |
-e |
PEM_OUTPUT_PATH |
. |
存放证书、私钥与 p12 的目录(可选) |
从源码结构看,几个默认值的行为细节值得注意:
- 默认生成三个文件。在 pem/lib/pem/manager.rb 的
create_certificate中:先按save_private_key(默认true)写出.pkey私钥;再按generate_p12(默认true)用OpenSSL::PKCS12写出.p12;最后写出.pem,其内容是 X.509 证书的 PEM + 私钥 PEM 的拼接(x509_certificate.to_pem + pkey.to_pem)。 - 文件名规则:不指定
pem_name时,文件名为{certificate_type}_{app_identifier}_{platform},例如production_com.example.app_ios.pem;证书类型前缀由certificate_type方法决定,取值为production、development、website或voip。这个命名规则在测试用例 pem/spec/manager_spec.rb 中被明确断言,例如production_com.krausefx.app_ios.pem、voip_com.krausefx.app_ios.pem、production_com.krausefx.app_macos.pem。 - Appfile 回退:
app_identifier、username、team_id、team_name的默认值均通过CredentialsManager::AppfileConfig.try_fetch_value动态取自项目中的Appfile,因此把 Apple 账号信息写进Appfile后,CI 上运行pem就无需再传-u、-a。 - team 选项的环境变量联动:
team_id与team_name的verify_block会同步写入ENV["FASTLANE_TEAM_ID"]/ENV["FASTLANE_TEAM_NAME"],供 Spaceship 登录选团队时使用。
在 Fastfile 中使用 get_push_certificate
pem 命令对应的 action 是 fastlane/lib/fastlane/actions/get_push_certificate.rb 中的 GetPushCertificateAction,而 fastlane/lib/fastlane/actions/pem.rb 中的 PemAction 只是它的别名(描述明确写着 "Alias for the get_push_certificate action")。该 action 在 CLI 选项之外还支持一个 new_profile 回调,仅当确实生成了新证书时才会被调用,非常适合用来自动把新 .pem 上传到推送服务器:
get_push_certificate(
force: true, # 即使旧证书仍有效也创建新的
app_identifier: "net.sunapps.9", # 可选的 App Bundle ID
save_private_key: true,
new_profile: proc do |profile_path|
puts profile_path # 新生成 PEM 文件的绝对路径
# 在此插入上传 PEM 文件到服务器的代码
end
)
action 层还会把 PEM::Manager.start 的返回值(新证书路径)透传给回调;在 Helper.test?(单元测试模式)下则直接使用 ./test.pem 占位路径,避免测试时真实访问 Apple 服务端——这一分支同样可以在源码中看到。
空密码 p12 与 Keychain Access 的注意事项
pem 允许不指定密码(或空字符串密码)生成合法的 p12 文件:源码中 p12_password == "" ? nil 的写法(见 pem/lib/pem/manager.rb 第 82 行附近)表明空密码会被转换为 nil 传给 OpenSSL,生成无密码 p12。
但要注意:macOS 的 Keychain Access 无法直接打开这种无密码 p12(即使文件本身是合法的)。可以用 OpenSSL 校验:
openssl pkcs12 -info -in my.p12
如果你必须把 p12 导入钥匙串(例如用 Knuff 或 Pusher 之类的应用测试推送),可以用 OpenSSL 把 p12 转成 pem 再转回带密码的 p12:
% openssl pkcs12 -in my.p12 -out my.pem
Enter Import Password:
<直接回车:该 p12 没有密码>
MAC verified OK
Enter your pem passphrase:
<输入一个临时密码用于加密 pem 文件>
% openssl pkcs12 -export -in my.pem -out my-with-passphrase.p12
Enter pass phrase for temp.pem:
<输入刚才的临时密码解密 pem 文件>
Enter Export Password:
<输入用于加密新 p12 文件的密码>
工作流程与底层实现
pem 通过 spaceship 与 Apple Developer Portal 通信。核心流程在 pem/lib/pem/manager.rb 中,可拆为三步:
- 登录:
login方法调用Spaceship.login(PEM.config[:username], nil)后用Spaceship.client.select_team选定团队。用户名可来自命令行、PEM_USERNAME或 Appfile,密码由 fastlane 的 credentials_manager(CredentialsManager)统一管理——这也是文档中 "How is my password stored?" 一节的实现依据:密码不会明文写在 Fastfile 里,而是通过 fastlane 的凭据管理器读取。 - 检查现有证书:
start方法会拉取certificate.all并按到期时间倒序排序(certificate_sorted),匹配owner_name == app_identifier的现有证书,计算剩余天数。剩余天数超过active_days_limit(默认 30)且未设置force时,直接提示 "No need to create a new one" 并返回,不做任何创建动作。 - 创建并落盘:
create_certificate先调用Spaceship.certificate.create_certificate_signing_request在本机生成 CSR 与 RSA 私钥,再用certificate.create!(csr: csr, bundle_id: ...)提交到 Portal。如果 Apple 返回 "You already have a current..."(同一 App/环境已存在 2 个活跃推送证书),会给出明确的user_error!提示:需要先吊销旧证书才能创建新证书。成功后cert.download取回 X.509 证书,再按上文所述落盘.pkey、.p12、.pem。
证书类型的路由逻辑同样在这份源码里(certificate 方法):website_push → Spaceship.certificate.website_push;voip_push → voip_push;否则按 platform(ios / macos)与 development 开关映射到 production_push、development_push、mac_production_push、mac_development_push 四种之一,其他平台值会报 "Unsupported platform" 错误。
相关的证书生命周期管理
pem 只处理推送证书。若要同时自动化 App Store 开发证书 的创建与续期,可配合 cert 工具(cert/lib/cert/runner.rb);若要自动化 Provisioning Profile 的创建与分发,可使用 match(match/lib/match/runner.rb)。三者组合后,推送、签名、描述文件即可全部纳入 CI。
小结
fastlane pem一条命令完成推送证书 CSR 生成、申请、下载与.pem落盘,并默认额外输出.p12与.pkey;- 现有证书有效期超过 30 天(
active_days_limit可调)时自动跳过创建,--force可强制新建;pem永不吊销现有证书; - 支持生产/开发/Website Push/VoIP 四种证书类型,以及
ios/macos双平台; - 在 Fastfile 中应优先使用
get_push_certificateaction 及其new_profile回调,把新证书自动上传到你的推送服务器; - Apple 账号凭据建议放入
Appfile并交由 fastlane 的 credentials_manager 管理,参数细节以fastlane action pem输出为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
