关于 axiom-code-signing
面向 iOS/macOS 开发者的代码签名配置技能,涵盖证书、描述文件、权限和 CI/CD 分发准备,并说明自动与手动签名及团队管理流程。
主要功能
- 01证书与描述文件管理
- 02配置签名权限
- 03设置 CI/CD 签名
- 04准备应用分发构建
适用场景
- 01为新项目设置代码签名
- 02排查证书或描述文件错误
- 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 Store | Apple Distribution | 每个账户最多 3 个 |
| Ad Hoc 分发 | Apple Distribution | 使用相同证书,但描述文件类型不同 |
| 在 App Store 之外分发 macOS 应用 | Developer ID Application | 有效期为 5 年 |
证书续期工作流
证书有效期为 1 年(Developer ID 证书为 5 年)。证书过期后:
- 不要撤销过期证书——证书已经过期;撤销会将其从门户中移除,并使仍引用该证书的描述文件失效
- 在 Apple Developer Portal 中创建新证书
- 下载并安装新证书
- 编辑使用旧证书的所有描述文件 → 选择新证书 → 重新生成
- 下载并安装更新后的描述文件
- 使用新 .p12 文件和描述文件更新 CI
- 如果使用 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 会进行合规审计——滥用(向公众分发)会导致计划资格终止
权限配置
添加新功能
- Apple Developer Portal:App IDs → 选择你的 App ID → Capabilities → 启用该功能
- Xcode:Target → Signing & Capabilities → + Capability → 选择功能
- 重新生成描述文件:如果使用手动签名,请编辑描述文件以包含新功能,然后生成并下载
如果使用自动签名,Xcode 会自动处理步骤 1 和 3。
常见功能 → 权限映射
| 功能 | 权限键 | 配置文件要求 |
|---|---|---|
| 推送通知 | aps-environment | 必须包含在配置文件中 |
| App Groups | com.apple.security.application-groups | 必须包含在配置文件中 |
| Associated Domains | com.apple.developer.associated-domains | 必须包含在配置文件中 |
| 使用 Apple 登录 | com.apple.developer.applesignin | 必须包含在配置文件中 |
| HealthKit | com.apple.developer.healthkit | 必须包含在配置文件中 |
| iCloud | com.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。关键步骤:
- 创建临时 keychain
- 添加到搜索列表(包含 login.keychain-db)——切勿使用
default-keychain -s,因为这会导致无法访问 login keychain 中的凭据 - 解锁 keychain
- 导入 .p12 证书
- 设置分区列表(防止 errSecInternalComponent)
- 安装 provisioning profile 到
~/Library/MobileDevice/Provisioning Profiles/ - 使用明确的签名设置构建/归档
- 清理——删除临时 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