mcpField
A
axiom-code-signing

axiom-code-signing

github.com/charleswiltgen开发1.2k57 次安装今天更新
代码仓库

关于 axiom-code-signing

面向 iOS/macOS 开发者的代码签名配置技能,涵盖证书、描述文件、权限和 CI/CD 分发准备,并说明自动与手动签名及团队管理流程。

主要功能

  1. 01证书与描述文件管理
  2. 02配置签名权限
  3. 03设置 CI/CD 签名
  4. 04准备应用分发构建

适用场景

  1. 01为新项目设置代码签名
  2. 02排查证书或描述文件错误
  3. 03配置 App Store 或 Ad Hoc 分发

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-code-signing

安装后在 Claude Code 中输入/axiom-code-signing确认已加载。

详细介绍

代码签名

为 iOS/macOS 应用管理证书、配置描述文件和权利配置,设置 CI/CD 签名,以及准备分发构建。

适用场景

在以下情况下使用:

  • ☑ 为新项目或 CI/CD 流水线设置代码签名
  • ☑ 调试任何代码签名错误(证书、配置描述文件、权利、钥匙串)
  • ☑ 为 App Store、TestFlight 或 Ad Hoc 分发配置签名
  • ☑ 在团队内管理证书和配置描述文件
  • ☑ 设置 fastlane match,以便团队统一管理证书
  • ☑ 了解自动签名与手动签名的利弊
  • ☑ 添加需要更改权利的新功能
  • ☑ 修复 CI/CD 签名失败(errSecInternalComponent、钥匙串已锁定)

示例提示词

“如何为我的应用设置代码签名?” “我的构建失败,并提示‘No signing certificate found’” “如何为团队设置 fastlane match?” “GitHub Actions 中出现 errSecInternalComponent” “上传到 App Store 时出现 ITMS-90035” “如何添加推送通知权利?” “有多个证书匹配——身份不明确” “代码签名在本地正常,但在 CI 中失败” “如何为 App Store 分发签名?” “我的配置描述文件已过期,该怎么办?”

风险信号

以下情况表明你让问题变得不必要地复杂:

  • ❌ 通过 Slack/电子邮件共享证书——在聊天中发送 .p12 文件会带来安全风险和版本混乱。请使用 fastlane match 或 Xcode 的自动签名。
  • ❌ 为了“修复”构建而禁用代码签名——设备或分发构建无法禁用代码签名。错误仍会再次出现。
  • ❌ 将 .p12 或 .mobileprovision 提交到 git——这些都是机密信息。请使用 CI 机密管理(GitHub Secrets、环境变量)。
  • ❌ 为了“保险起见”重新生成所有证书——这会吊销现有证书,导致所有团队成员和 CI 流水线无法正常工作。
  • ❌ 在团队/CI 构建中使用个人证书——该人员离职或证书过期时,一切都会出问题。
  • ❌ 忽略“配置描述文件不包含签名证书”的警告——这必然会导致构建失败。请立即修复。
  • ❌ 设置 CODE_SIGN_IDENTITY = "" 来抑制错误——这只会把问题推迟到归档/导出阶段,届时更难调试。
  • ❌ 为自动签名项目手动管理配置描述文件——请选择一种方式。混用会引发冲突。

必须先完成的步骤

在配置或调试任何代码签名问题之前:

1. 列出可用的签名身份
security find-identity -v -p codesigning

这会告诉你已安装、有效且可用于签名的证书。

2. 解码配置描述文件
# Find profile embedded in most recent build
find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -3

# Decode it
security cms -D -i path/to/embedded.mobileprovision

检查:过期时间、内嵌证书、权利、设备列表。

3. 从二进制文件中提取权利
codesign -d --entitlements - /path/to/MyApp.app

将这些内容与配置描述文件的权利以及你的 .entitlements 文件进行比对。三者必须一致。

4. 验证配置描述文件中的证书
# Get SHA-1 from keychain
security find-identity -v -p codesigning | grep "Apple Distribution"
# Output: 1) ABCDEF123... "Apple Distribution: Company (TEAMID)"

# Get SHA-1 from profile
security cms -D -i embedded.mobileprovision | plutil -extract DeveloperCertificates xml1 -o - -
echo "<base64 data>" | base64 -d | openssl x509 -inform DER -noout -fingerprint -sha1

SHA-1 哈希必须匹配。如果不匹配,说明配置描述文件生成时使用的证书与钥匙串中的证书不同。

自动签名与手动签名

digraph signing_decision {
    "New project or\nsmall team?" [shape=diamond];
    "CI/CD pipeline?" [shape=diamond];
    "Multiple targets\nwith different profiles?" [shape=diamond];
    "Team > 3 developers?" [shape=diamond];

    "Automatic Signing" [shape=box, label="Use Automatic Signing\nXcode manages everything"];
    "Manual + match" [shape=box, label="Use Manual Signing\n+ fastlane match"];
    "Manual Signing" [shape=box, label="Use Manual Signing\nspecify profiles explicitly"];
    "Automatic + overrides" [shape=box, label="Use Automatic Signing\nwith xcodebuild overrides\nfor CI"];

    "New project or\nsmall team?" -> "CI/CD pipeline?" [label="no, larger team"];
    "New project or\nsmall team?" -> "Automatic Signing" [label="yes, solo/2-3 devs"];
    "CI/CD pipeline?" -> "Team > 3 developers?" [label="yes, have CI"];
    "CI/CD pipeline?" -> "Multiple targets\nwith different profiles?" [label="no CI"];
    "Team > 3 developers?" -> "Manual + match" [label="yes"];
    "Team > 3 developers?" -> "Automatic + overrides" [label="no, small team + CI"];
    "Multiple targets\nwith different profiles?" -> "Manual Signing" [label="yes"];
    "Multiple targets\nwith different profiles?" -> "Automatic Signing" [label="no"];
}
自动签名

最适合:独立开发者、小型团队、没有 CI 的项目。

Xcode 会自动管理证书和配置描述文件。你只需选择一个团队。

Xcode → Target → Signing & Capabilities → ✓ Automatically manage signing → Select Team

工作方式:

  • Xcode 会按需创建/下载证书
  • 生成与功能相匹配的配置描述文件
  • 添加/移除功能时重新生成配置描述文件
  • 连接设备时注册设备

限制:

  • 每台机器只能使用一位开发者的证书(团队中可能发生冲突)
  • CI 需要通过 xcodebuild 覆盖参数或使用 Xcode Cloud
  • 难以在团队成员之间共享配置描述文件
  • 存在多个可用配置描述文件时,可能会选择意料之外的配置描述文件
手动签名

适用场景:团队、CI/CD 流水线、包含多个目标/扩展的应用。

你可以明确指定要使用的证书和描述文件。

Xcode → Target → Signing & Capabilities → ✗ Automatically manage signing
→ Select Provisioning Profile for each configuration (Debug/Release)

工作原理:

  • 在 Apple Developer Portal 中创建证书和描述文件
  • 手动下载并安装描述文件(或使用 match)
  • 在 Xcode 或 xcodebuild 中指定确切的描述文件
  • 完全控制使用的证书/描述文件组合

必需的构建设置:

CODE_SIGN_STYLE = Manual
CODE_SIGN_IDENTITY = Apple Distribution: Company Name (TEAMID)
PROVISIONING_PROFILE_SPECIFIER = MyApp App Store Profile
DEVELOPMENT_TEAM = YOURTEAMID

证书类型和生命周期

证书类型选择
场景证书类型备注
在设备上进行调试构建Apple Development由 Xcode 自动创建
TestFlight / App StoreApple Distribution每个账户最多 3 个
Ad Hoc 分发Apple Distribution使用相同证书,但描述文件类型不同
在 App Store 之外分发 macOS 应用Developer ID Application有效期为 5 年
证书续期工作流

证书有效期为 1 年(Developer ID 证书为 5 年)。证书过期后:

  1. 不要撤销过期证书——证书已经过期;撤销会将其从门户中移除,并使仍引用该证书的描述文件失效
  2. 在 Apple Developer Portal 中创建新证书
  3. 下载并安装新证书
  4. 编辑使用旧证书的所有描述文件 → 选择新证书 → 重新生成
  5. 下载并安装更新后的描述文件
  6. 使用新 .p12 文件和描述文件更新 CI
  7. 如果使用 fastlane match:执行 fastlane match nuke [type],然后执行 fastlane match [type]

团队协调:重新生成前通知团队。分发证书由团队共享——重新生成证书会影响所有人。

描述文件模式

开发描述文件

用于在已注册设备上进行调试构建:

  • 包含:开发证书 + 已注册设备的 UDID + App ID + 权限
  • 创建方式:由 Xcode 自动创建(自动签名),或在门户中手动创建
  • 设备:必须在门户中明确注册(每种类型每年最多 100 台设备)
Ad Hoc 描述文件

用于在指定设备上进行测试,无需使用 TestFlight:

  • 包含:分发证书 + 已注册设备的 UDID + App ID + 权限
  • 使用场景:QA 测试、客户演示、不使用 TestFlight 的 Beta 测试
  • 限制:与开发描述文件相同,每年最多 100 台设备
App Store 描述文件

用于提交到 TestFlight 和 App Store:

  • 包含:分发证书 + App ID + 权限(无设备列表)
  • 无设备数量限制——TestFlight 最多支持 10,000 名测试者
  • 使用 Xcode Organizer 上传或执行 xcodebuild -exportArchive 时必需
企业描述文件

用于内部发布(仅限 Apple Developer Enterprise Program):

  • 包含:企业证书 + App ID + 权限(无设备列表)
  • 无需注册设备即可在内部发布
  • 不能提交到 App Store
  • Apple 会进行合规审计——滥用(向公众分发)会导致计划资格终止

权限配置

添加新功能
  1. Apple Developer Portal:App IDs → 选择你的 App ID → Capabilities → 启用该功能
  2. Xcode:Target → Signing & Capabilities → + Capability → 选择功能
  3. 重新生成描述文件:如果使用手动签名,请编辑描述文件以包含新功能,然后生成并下载

如果使用自动签名,Xcode 会自动处理步骤 1 和 3。

常见功能 → 权限映射
功能权限键配置文件要求
推送通知aps-environment必须包含在配置文件中
App Groupscom.apple.security.application-groups必须包含在配置文件中
Associated Domainscom.apple.developer.associated-domains必须包含在配置文件中
使用 Apple 登录com.apple.developer.applesignin必须包含在配置文件中
HealthKitcom.apple.developer.healthkit必须包含在配置文件中
iCloudcom.apple.developer.icloud-*必须包含在配置文件中
App 内购买自动无需更改配置文件
后台模式UIBackgroundModes(Info.plist)无需更改配置文件
Keychain 共享keychain-access-groups必须包含在配置文件中
多目标权限

每个目标(应用、widget、扩展等)都需要各自的:

  • Developer Portal 中的 App ID
  • Provisioning profile
  • .entitlements 文件

所有目标必须使用相同的 Team ID。App Groups 支持在目标之间共享数据。

com.example.app                  → MyApp.entitlements
com.example.app.widget           → Widget/Widget.entitlements
com.example.app.NotificationService → NotificationService/NotificationService.entitlements

CI/CD 签名设置

不使用 fastlane(原始脚本)

完整的 CI keychain 脚本请参阅 axiom-code-signing-ref。关键步骤:

  1. 创建临时 keychain
  2. 添加到搜索列表(包含 login.keychain-db)——切勿使用 default-keychain -s,因为这会导致无法访问 login keychain 中的凭据
  3. 解锁 keychain
  4. 导入 .p12 证书
  5. 设置分区列表(防止 errSecInternalComponent)
  6. 安装 provisioning profile 到 ~/Library/MobileDevice/Provisioning Profiles/
  7. 使用明确的签名设置构建/归档
  8. 清理——删除临时 keychain(始终执行,即使失败也要执行)
使用 fastlane match

fastlane match 会在共享 git 仓库(或云存储)中管理证书和配置文件,并使用密码短语加密。

初始设置(由团队管理员运行一次):

fastlane match init              # Choose storage (git, google_cloud, s3)
fastlane match development       # Generate dev certs + profiles
fastlane match appstore          # Generate distribution certs + profiles

CI 用法(只读——切勿在 CI 中生成):

# Fastfile
lane :release do
  setup_ci                        # Creates temporary keychain
  match(type: "appstore", readonly: true)
  build_app(scheme: "MyApp", export_method: "app-store")
end

关键规则:

  • CI 必须使用 readonly: true——切勿在 CI 中生成证书
  • 将 MATCH_PASSWORD 设置为 CI 密钥(用于加密/解密证书仓库)
  • 使用 setup_ci 创建临时 keychain(会处理所有 keychain 设置)
  • 每个应用目标都需要在 Matchfile 中配置自己的 app_identifier
Xcode Cloud

Xcode Cloud 会自动处理签名:

  • 无需管理证书——Apple 会管理签名基础设施
  • 在 Xcode → Product → Xcode Cloud → Manage Workflows 中配置
  • 使用 ci_post_clone.sh 进行自定义设置(SPM 身份验证、来自自定义来源的证书)
  • 分发签名在“Archive”操作期间处理

反模式

反模式 1:通过聊天分享 .p12

错误:

Slack: "Hey, here's the distribution cert 📎 cert.p12, password is Company123"

正确:使用 fastlane match(加密的 git 仓库),或使用共享团队帐户通过 Xcode 自动签名。

重要原因:通过聊天分享 .p12 文件会带来安全风险(凭据留存在聊天记录中)、版本混乱(哪个证书是当前版本?)以及单点故障(如果发送者的证书过期或被撤销)。共享证书出问题时,每位团队成员需要花费 1-2 小时处理。

反模式 2:将密钥提交到 Git

错误:

git add certificates/distribution.p12
git add profiles/AppStore.mobileprovision
git commit -m "Add signing files"

正确:使用 CI 密钥管理:

# GitHub Actions
echo "$P12_BASE64" | base64 --decode > certificate.p12   # From secrets
echo "$PROFILE_BASE64" | base64 --decode > profile.mobileprovision

重要原因:证书和配置文件都是密钥——任何人都可以用它们将应用签名为你的团队。即使是私有仓库,git 历史记录也会永久保留。提交后,每个克隆版本都会永远包含该凭据。

如果已提交:使用 git filter-repo --path path/to/cert.p12 --invert-paths 清除历史记录,然后轮换已泄露的证书(在 portal 中撤销、创建新证书并更新 CI 密钥)。所有现有克隆版本仍然包含旧证书——应将其视为已泄露。

反模式 3:“修复”签名的方法是禁用签名

错误:

CODE_SIGN_IDENTITY = ""
CODE_SIGNING_REQUIRED = NO
CODE_SIGNING_ALLOWED = NO

正确:按照强制诊断步骤,找出实际的签名错误。

重要原因:禁用代码签名在 Simulator 构建中“有效”,但在设备构建、归档和分发时会失败。问题被拖到更难调试、也更临近截止日期的阶段才处理。时间成本:原始问题的处理时间,加上 30 多分钟的导出和上传调试时间。

反模式 4:在团队或 CI 中使用个人证书

错误:

# CI pipeline uses one developer's personal certificate
security import ~/charles-personal.p12 ...
CODE_SIGN_IDENTITY = "Apple Distribution: Charles Personal (ABC123)"

正确:使用通过 fastlane match 管理的专用团队证书,或共享的 Apple Developer 账户:

match(type: "appstore", readonly: true)

重要原因:如果该开发者离职、更换设备,或证书过期,CI 和整个团队的分发都会中断。绝不应将某个人的个人证书作为共享基础设施的依赖项。时间成本:当此人无法联系时,紧急轮换证书需要 2–4 小时。

压力场景

场景 1:“先禁用代码签名,这样我们今天就能发布”

背景:构建截止日期临近,代码签名错误导致无法归档。

压力:“我们测试不需要签名,先禁用它,发布后再修复。”

实际情况:没有有效的代码签名,就无法提交到 TestFlight 或 App Store。现在禁用签名,会把一个阻塞问题拖到时间压力最大的时刻——实际提交时。诊断步骤只需 5–10 分钟,就能找出根本原因。

正确做法:执行强制初始步骤中的第 1–4 步。大多数签名问题都只是某个组件过期或不匹配。

回应模板:“没有有效签名,我们就无法提交到 TestFlight 或 App Store。诊断只需 5 分钟,就能准确告诉我们出了什么问题。现在禁用签名,意味着提交时还会遇到同一个阻塞问题,而且留给我们的时间更少。”

场景 2:“用我的个人证书来构建团队版本”

背景:分发证书过期,导致 CI 出故障。一名团队成员提出用自己的个人证书快速解决。

压力:“我有可用的证书,先用我的吧。”

实际情况:使用个人证书会造成单点故障。如果此人无法联系、正在休假或离开公司,签名就会中断;没有他们的配合,也无从恢复。正确的修复方法(续期团队证书)耗时相同。

正确做法:在 Apple Developer Portal 中续期团队的分发证书。更新 CI 密钥。使用新证书重新生成配置描述文件。

回应模板:“短期来看,你的证书确实能用,但这会让你成为所有构建的单点故障。续期团队证书同样只需 10 分钟,也不会让我们依赖某一个人。”

场景 3:“把 .p12 提交到仓库,这样 CI 就能用”

背景:首次设置 CI/CD。开发者想为了方便将证书提交到 git。

压力:“这是私有仓库,没人看得到。”

实际情况:Git 历史是永久的。即使是私有仓库,包含私钥的 .p12 也会永远存在于每个克隆、每个分支和每份备份中。如果仓库被设为公开、开源或提供给承包商访问,签名密钥就会暴露。CI 密钥管理正是为此而存在。

正确做法:将 .p12 编码为 Base64,并存储为 CI 密钥(GitHub Secrets、GitLab CI Variables 等)。在构建时解码。

回应模板:“Git 历史是永久的——一旦提交,证书就会永远留在每个克隆中。配置 CI 密钥管理(GitHub Secrets)的工作量相同,而且它正是为这种情况设计的。让我来正确配置。”

检查清单

归档以供分发前:

证书:

  • 分发证书有效(未过期、未吊销)
  • security find-identity -v -p codesigning 显示预期的身份
  • 证书与配置描述文件要求的证书相符

配置描述文件:

  • 配置描述文件未过期
  • 配置描述文件类型与分发方式相符(App Store、Ad Hoc、Enterprise)
  • 配置描述文件包含用于签名的证书
  • 配置描述文件的 Bundle ID 与目标的 Bundle ID 相符

权利:

  • Xcode 中的所有功能与配置描述文件中的功能相符
  • .entitlements 文件不包含配置描述文件中没有的功能
  • App 扩展具有自己的配置描述文件,且权利设置正确
  • 主 App 与扩展中的 App Groups 保持一致

构建设置:

  • CODE_SIGN_STYLE 与预期一致(Automatic 或 Manual)
  • CODE_SIGN_IDENTITY 与构建类型相符(Development 或 Distribution)
  • 手动签名时设置了 PROVISIONING_PROFILE_SPECIFIER
  • DEVELOPMENT_TEAM 设置为正确的 Team ID

CI/CD(如适用):

  • 已创建并解锁 Keychain,且已设置分区列表
  • 证书已导入 CI Keychain
  • 配置描述文件已安装到 ~/Library/MobileDevice/Provisioning Profiles/
  • 成功和失败时都会运行清理步骤(if: always())

资源

WWDC:2021-10204、2022-110353

文档:/security、/bundleresources/entitlements、/xcode/distributing-your-app

技能:axiom-code-signing-ref、axiom-code-signing-diag

评论

支持 Markdown