首页
/ fastlane cert 深度解析:iOS 代码签名证书的自动创建、查找与吊销

fastlane cert 深度解析:iOS 代码签名证书的自动创建、查找与吊销

2026-09-05 15:31:39作者:申梦珏Efrain

本文围绕 fastlane 中 get_certificates(别名 cert)action 的官方文档展开,讲清这个 action 的完整行为流程、全部命令行参数与子命令、与 sigh 的联动机制,以及证书在本机 Keychain 中的验证原理。读完后,你可以独立完成证书自动化配置,并能从源码层面理解「何时新建证书、何时复用现有证书、何时抛出异常」的判定逻辑。

一、cert 是什么:定位与适用边界

cert 是 fastlane 工具链中负责 iOS 代码签名证书(code signing certificate) 管理的工具,它属于 fastlane 的 code_signing 分类(见 get_certificates.rbself.category 返回 :code_signing),且仅在 :ios 平台生效(is_supported? 判断 platform == :ios)。

官方文档中有一个必须重视的提示:推荐使用 match 来生成和维护证书cert 只在你需要完全掌控每一步、并且熟悉代码签名细节时才建议直接使用。这是因为 cert 的操作粒度更细,它把「查找、创建、导入 Keychain」的每个环节都暴露给了使用者,适合需要自定义证书策略的 CI 场景。

二、基本用法与行为语义

最简命令是:

fastlane cert

执行后它会检查本地机器上是否已安装可用的签名证书。只有当确实需要创建新证书时,cert 才会依次执行以下四步(见 runner.rbcreate_certificate 方法):

  1. 创建一个新的私钥(private key);
  2. 创建一个新的证书签名请求(CSR,certificate signing request);
  3. 生成、下载并安装证书;
  4. 把生成的所有文件导入本地 Keychain。

三条关键行为语义:

  • cert 永远不会吊销你现有的证书。 当你无法再创建更多证书时,cert 会抛出异常——此时你必须手动吊销一个已有证书来腾出名额。这一点在源码中得到印证:create_certificate 捕获创建失败后,若错误信息包含 "You already have a current",会明确报出「已达到该类型证书数量上限」(见 runner.rb);若包含 "You are not allowed to perform this operation." 且创建的是 Distribution 证书,则提示「只有 Team Admin 才能创建 Distribution 证书」。

  • Apple ID 可以通过 -u 传入:

    fastlane cert -u cert@example.com
    
  • 可以用 fastlane action cert 查看全部可用命令与环境变量。 该命令由 fastlane 的命令文档系统生成,其参数列表直接来自 Cert::Options.available_options(见 get_certificates.rbavailable_options 的实现)。

还有一条文档中的硬性限制:cert 无法从 Apple Developer Portal 下载已有证书加私钥——因为私钥永远不会离开你的电脑。所以 cert 的复用逻辑是「本地已安装 / 本地有私钥缓存」两种情况(下文详述),而不是去门户下载私钥。

三、运行流程源码解析:launch → run → find / create

cert 的完整执行链是:action 层调用 Cert::Runner.new.launch(见 get_certificates.rb),Runner 内部流程如下(见 runner.rb):

launch 阶段:先执行 run 主流程,然后在 macOS 上用 FastlaneCore::CertChecker.installed? 验证新证书是否已正确装入 Keychain;若非 macOS(例如 Linux CI),则跳过验证并打印提示。

run 阶段的决策逻辑:

  1. 确保输出目录存在(FileUtils.mkdir_p(Cert.config[:output_path])),并打印本次运行的配置摘要表;
  2. login 登录 App Store Connect:若提供了 api_key / api_key_path(App Store Connect API Key),走 token 认证,用户名变为可选;否则回退到 Apple ID 登录,此时会强制要求提供 usernameCert.config.fetch(:username, force_ask: true));
  3. 判定是否需要新建证书:若 force: true 直接新建;否则调用 find_existing_cert 查找,只有查不到才新建;
  4. create_certificate 成功即返回,失败则报 UI.user_error!

find_existing_cert 的三级判定(见 runner.rb)是理解「cert 何时不新建证书」的关键:

顺序 条件 行为
1 证书内容存在,且 CertChecker.installed? 判定该证书已装入本机 Keychain 记录环境变量,直接复用,提示 "Found the certificate … which is installed on the local machine"
2 本机未安装,但输出目录里存在同名 {certificate.id}.p12 私钥缓存 通过 FastlaneCore::KeychainImporter 把私钥和证书重新导入 Keychain 后复用,提示 "Found the cached certificate …"
3 两者都不满足 记录错误、删除无用的裸证书文件,最终提示 "Couldn't find an existing certificate… creating a new one",进入新建流程

这个设计解释了文档那句话:cert 先检查本地是否有可用签名证书,只有必要时才走「建私钥 → 建 CSR → 下载安装 → 导入 Keychain」的完整链路。

四、全部参数与环境变量(Options 全表)

运行 fastlane action cert 可列出所有参数,其定义集中在 cert/lib/cert/options.rb。以下按源码逐项整理:

参数 短选项 环境变量 类型 / 默认值 说明
development - CERT_DEVELOPMENT Boolean,默认 false 创建 Development 证书而非 Distribution 证书
type - CERT_TYPE 字符串,可选 指定具体证书类型,优先于 development。取值:mac_installer_distributiondeveloper_id_installerdeveloper_id_applicationdeveloper_id_kext
force - CERT_FORCE Boolean,默认 false 即使已有证书也强制新建
generate_apple_certs - CERT_GENERATE_APPLE_CERTS Boolean,默认「macOS 且 Xcode ≥ 11」时自动为 true 创建 Xcode 11 及以后使用的 Apple Development / Apple Distribution 证书类型
api_key_path - CERT_API_KEY_PATH(兼容 DELIVER_API_KEY_PATHAPP_STORE_CONNECT_API_KEY_PATH 路径,可选 App Store Connect API Key 的 JSON 文件路径,与 api_key 互斥
api_key - CERT_API_KEY(兼容 DELIVER_API_KEYAPP_STORE_CONNECT_API_KEY Hash,敏感字段 以 hash 形式传入 API Key,与 api_key_path 互斥
username -u CERT_USERNAME 字符串,可选 Apple ID。默认值会动态取 Appfile 中的 apple_dev_portal_idapple_id
team_id -b CERT_TEAM_ID 字符串,可选 属于多个团队时指定团队 ID,默认取自 Appfile 的 team_id
team_name -l CERT_TEAM_NAME 字符串,可选 同上,按团队名指定,默认取自 Appfile 的 team_name
filename -q CERT_FILE_NAME 字符串,可选 证书落盘文件名的自定义;不带扩展名时会自动补 .cer
output_path -o CERT_OUTPUT_PATH 路径,默认 . 证书与私钥统一存放的目录
keychain_path -k CERT_KEYCHAIN_PATH 路径,可选 自定义 Keychain 路径。macOS 上默认为 ~/Library/Keychains/login.keychain(-db)非 macOS 平台传入会直接报错(Keychain 不支持)
keychain_password -p CERT_KEYCHAIN_PASSWORD 敏感字段,可选 首次在新 Mac 上访问证书时可能需要的密码(login 默认 Keychain 即 macOS 账户密码);非 macOS 平台传入会报错
skip_set_partition_list -P CERT_SKIP_SET_PARTITION_LIST Boolean,默认 false 跳过设置 partition list(有时很耗时)。通常应保留该步骤,否则 Xcode 可能反复弹窗要求允许证书用于签名
platform - CERT_PLATFORM 字符串,默认 ios 证书平台:iosmacostvos

几个值得注意的默认值行为(均可在 options.rb 中核对):

  • generate_apple_certs动态默认值:只要你在 macOS 上用 Xcode 11+ 运行,cert 就会默认创建 Apple Distribution / Apple Development 类型。测试代码 runner_spec.rb 正好覆盖了这一分叉:Xcode 10 用 Spaceship.certificate.production,Xcode 11 用 apple_distribution
  • 证书类型如何由参数推导,见 runner.rbcertificate_typestype 参数最高优先级;否则看 generate_apple_certs(Enterprise/in-house 团队不使用 Apple Distribution,会回落到 iOS Distribution;development: true 时用 Development 类型);最后按 platform 选择 iOS / macOS 的 Distribution 或 Development 类型。
  • 一个已知限制(源码中明确 raise):截至源码注释的时间点,App Store Connect API 不允许通过 API Key 访问 DEVELOPER_ID_INSTALLER 类型,该类型只能走 Apple ID 登录路径。

五、CLI 子命令:create 与 revoke_expired

除了作为 action 在 Fastfile 中使用,cert 还有独立的 CLI(见 commands_generator.rb):

fastlane cert create           # 默认子命令,等价于 fastlane cert
fastlane cert revoke_expired   # 吊销已过期的证书

revoke_expired 对应 Cert::Runner#revoke_expired_certs!(见 runner.rb):它登录 App Store Connect 后,用 certificates.reject(&:valid?) 筛出过期证书逐个调用 delete!。实现上有两个健壮性细节:单张证书吊销失败不会中断,会 UI.error 后继续吊销其余证书;结束时汇总吊销数量。runner_spec.rb 中有两条测试专门验证「正确挑选过期证书」和「一张失败不影响其余吊销」。

这条子命令恰好补上了文档中「cert 从不吊销你现有证书」留下的运维缺口:它只吊销过期的,绝不主动吊销仍有效的证书。

六、共享值与 sigh 联动

在 Fastfile 中,cert 执行完成后会向 lane_context 写入两个共享值(见 get_certificates.rb):

共享值 含义
CERT_FILE_PATH 证书文件路径
CERT_CERTIFICATE_ID 证书 ID

同时它会把证书 ID 写入环境变量 ENV["SIGH_CERTIFICATE_ID"],注释写明「for further use in the sigh action」。而 sigh 的选项定义SIGH_CERTIFICATE_ID 正是一个可用的环境变量名——这就是两条命令之间的隐式握手cert 刚安装的证书 ID 会被紧随其后的 sigh 直接采用。

文档推荐的经典组合是把两者放进同一个 lane:

lane :beta do
  cert
  sigh(force: true)
end

其中 force: truesigh 每次运行都重新生成描述文件(provisioning profile),从而保证始终使用当前本机已安装的、正确的签名证书。文档中的组合示例 cert && sigh 表达的就是同样的语义:先确保证书存在,再确保描述文件与证书匹配。

七、Keychain 验证与 WWDR 中间证书

「cert 检查本地证书是否安装」这一步由 fastlane_core/lib/fastlane_core/cert_checker.rb 实现,其中有几个对排障很重要的事实:

  • CertChecker.installed?(path, in_keychain:) 的计算方式是:取证书文件的 SHA-1 指纹sha1_fingerprint),与 security find-identity -v -p codesigning 列出的本机有效身份比对(见 cert_checker.rb)。
  • 若机器上「0 valid identities found」,它会打印诊断建议:手动执行 security find-identity -v -p codesigning,并提示检查是否存在过期的 WWDR 中间证书这一经典问题。
  • 更关键的是 install_missing_wwdr_certificates:每次列取身份前,cert 相关流程会检查 Keychain 中是否安装齐了 Apple 的 WWDR 中间证书(G2–G6 等,见 cert_checker.rb 中的 WWDRCA_CERTIFICATES 清单),缺哪个就从 Apple 证书权威站点下载哪个并 security import 进去。WWDR 中间证书过期是「证书明明在、签名却报信任链错误」的高频根因,fastlane 在这里做了自动修复。

八、密码如何存储

cert 登录 App Store Connect 使用的凭据由 fastlane 的 CredentialsManager 统一管理(见 credentials_manager/README.md):

  • 默认情况下,Apple ID 密码存储在 macOS Keychain 中,只保存在你的本机,从不离开你的电脑;
  • 也可以用环境变量 FASTLANE_USER / FASTLANE_PASSWORD 传入凭据;
  • 设置 FASTLANE_DONT_STORE_PASSWORD"1" 可禁止密码写入 Keychain;
  • 也可以走 App Store Connect API Key(api_key / api_key_path)完全绕过 Apple ID 密码——这在 CI 中是推荐做法。

九、实用 Tips

  • 查看本机证书身份security find-identity -v -p codesigning,这是排查「证书装了但 fastlane/Xcode 找不到」问题的第一步。
  • 多团队协作:在多个开发团队之间切换时,用 -b team_id 或在 Appfile 中配置 team_id,避免登录歧义。
  • CI 中的 Keychain 参数keychain_path / keychain_password / skip_set_partition_list 三个参数只在 macOS 上有效,在 Linux 容器里传入会直接校验失败,CI 配置中注意区分平台。
  • 非 macOS 平台的限制launch 会跳过 Keychain 验证与导入(打印 "Skipping importing certificates as it would not work on this operating system."),也就是说证书的 Keychain 安装步骤本质上依赖 macOS 环境,Linux 上只能完成「生成 + 下载」部分。
  • 进阶查看描述文件:文档提到可以安装 ProvisionQL 这类 Finder 扩展来以 Quick Look 方式直观查看 mobileprovision 文件内容,方便核对描述文件绑定的证书是否与 cert 产出的一致。

十、小结

cert 的行为可以归纳为一张决策表:有 API Key 或 Apple ID 可登录 → 列出该类型全部证书 → 已在本机 Keychain 中则复用 → 有本地 p12 缓存则重导入复用 → 否则新建(CSR + 下载 + Keychain 导入)→ macOS 上再做 SHA-1 指纹验证force: true 则跳过查找直接新建。它从不主动吊销有效证书,数量上限到达时报错,而清理过期名额的活儿交给 fastlane cert revoke_expired。配合 lane_context 共享值与 SIGH_CERTIFICATE_ID 环境变量,certsigh 串联起来,就构成了 fastlane 中「证书 → 描述文件」的自动化基石。

登录后查看全文
热门项目推荐
相关项目推荐