关于 axiom-code-signing-diag
面向 iOS/macOS 开发者的代码签名故障诊断技能,通过检查证书、描述文件、权限和 Keychain 配置来定位构建及上传问题,并提供按症状分类的排查路径。
主要功能
- 01检查签名身份
- 02解码描述文件
- 03比较应用权限配置
- 04排查 CI Keychain 故障
- 05按错误症状提供决策树
适用场景
- 01诊断找不到签名证书
- 02排查 CI 中的签名失败
- 03定位权限或描述文件不匹配
- 04处理 App Store 上传拒绝
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-code-signing-diag安装后在 Claude Code 中输入/axiom-code-signing-diag确认已加载。
详细介绍
代码签名诊断
系统地排查代码签名失败问题:证书缺失、描述文件不匹配、CI 中的钥匙串问题、权限冲突,以及 App Store 上传被拒。
概述
核心原则:代码签名失败时,问题通常出在:
- 证书问题(已过期、缺失、类型错误、已吊销)— 30%
- 描述文件问题(已过期、缺少证书、App ID 错误、缺少功能)— 25%
- 权限不匹配(Xcode 中启用了某项功能,但描述文件中没有,反之亦然)— 15%
- 钥匙串问题(CI 中已锁定、errSecInternalComponent、分区列表)— 15%
- 归档/导出问题(导出方法错误、分发时证书类型错误)— 10%
- 签名身份不明确(存在多个匹配的证书,Xcode 选错了证书)— 5%
务必先验证证书、描述文件和权限,再重写构建设置或重新生成所有内容。
危险信号
表明存在代码签名特定问题的症状:
| 症状 | 可能原因 |
|---|---|
| "No signing certificate found" | 证书已过期、已吊销,或不在钥匙串中 |
| "Provisioning profile doesn't include signing certificate" | 描述文件使用的证书与钥匙串中的证书不同 |
| ITMS-90035 Invalid Signature | 使用开发证书而非分发证书签名 |
| ITMS-90161 Invalid Provisioning Profile | 描述文件已过期或与二进制文件不匹配 |
| errSecInternalComponent in CI | 钥匙串已锁定,或未调用 set-key-partition-list |
| "Ambiguous — matches multiple" | 存在多个同名有效证书(开发证书和过期证书) |
| "Entitlement not allowed by profile" | 在 Xcode 中添加了功能,但未重新生成描述文件 |
| "codesign wants to access key" dialog | 尚未授予 codesign 钥匙串访问权限 |
| Build works locally, fails in CI | 缺少钥匙串设置步骤(创建、解锁、设置分区列表) |
| "Profile doesn't match bundle ID" | Xcode 目标与描述文件中的 Bundle 标识符不匹配 |
| Export fails after successful archive | ExportOptions.plist 指定了错误的方法或描述文件 |
| App extension signing fails | 扩展需要自己的描述文件,并且团队和前缀必须匹配 |
反合理化
| 合理化说法 | 为什么行不通 | 时间成本 |
|---|---|---|
| “证书昨天还好好的” | 证书会过期,也可能被吊销。配置文件会因门户中的更改而自动重新生成。务必重新验证。 | 证书在夜间过期后,调试构建设置需花费 30-60 分钟 |
| “我重新生成所有东西吧” | 重新生成证书会吊销旧证书,导致其他团队成员和 CI 无法正常工作。先诊断问题。 | 2-4 小时 + 团队成员无法正常工作 + CI 流水线中断 |
| “我重置一下钥匙串” | 这会删除所有已存储的凭据(SSH 密钥、已保存的密码、其他证书)。应诊断具体证书。 | 花 1-2 小时恢复所有凭据 |
| “先禁用代码签名吧” | 对于设备构建或分发,无法禁用代码签名。之后你还会遇到同样的问题,而且时间更紧。 | 浪费时间,原问题仍未解决 |
| “这是 Xcode 的 Bug,我重装一下” | 代码签名是配置问题,不是 Xcode 的 Bug。重新安装不会更改你的证书或配置文件。 | 花 2-4 小时重装 Xcode,而配置仍然有问题 |
| “我用团队的配置描述文件吧” | Xcode 自动管理的通配符配置描述文件缺少特定授权(推送、App Groups)。需要这些功能的应用无法使用它。 | 花 30 多分钟才发现缺少功能 |
| “CI 之前能用,我们这边什么都没改” | Apple 会出于安全原因吊销证书。CI 运行器的 macOS 更新会改变钥匙串行为。配置描述文件会在 1 年后过期。 | 证书过期期间,花数小时反复说“但我们什么都没改” |
| “我先检查一下代码” | 代码签名错误绝不是代码 Bug,而是 100% 的配置问题——证书、配置描述文件、授权和钥匙串。 | 配置描述文件过期期间,花数小时调试正常运行的代码 |
| “把 build.keychain 设为默认钥匙串” | security default-keychain -s build.keychain 会将登录钥匙串替换为默认钥匙串,导致无法访问 SSH 密钥、已保存的密码和其他凭据。请改用 list-keychains -s。 | 花 30 多分钟恢复默认钥匙串 + 遭遇莫名其妙的 SSH/凭据故障 |
必须执行的第一步
更改构建设置或重新生成证书之前,先运行以下诊断:
步骤 1:检查签名身份
security find-identity -v -p codesigning
预期输出:
- 至少有一个有效身份,名称为“Apple Development”或“Apple Distribution”
- 每个身份都显示 SHA-1 哈希值、名称和 Team ID
问题:
- 有效身份为 0 个 → 未安装证书,或证书均已过期
- 只有“Apple Development”,但要创建归档 → 需要 Distribution 证书
- 多个条目名称相同 → 身份不明确(参见树 5)
步骤 2:解码配置描述文件
# Find the profile being used
find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -3
# Decode it
security cms -D -i path/to/embedded.mobileprovision
检查以下字段:
ExpirationDate→ 是否尚未过期?DeveloperCertificates→ 是否包含你当前的证书?Entitlements→ 是否包含应用使用的所有功能?ProvisionedDevices→ 是否包含测试设备的 UDID?(仅限 Development/Ad Hoc)Name→ 是否与 Xcode 配置使用的名称一致?
步骤 3:提取并比较授权
# What entitlements does the built app have?
codesign -d --entitlements - /path/to/MyApp.app
# What entitlements does the profile grant?
security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -
# What entitlements does Xcode's .entitlements file declare?
cat MyApp/MyApp.entitlements
这三者必须一致。 任何不匹配都会导致签名失败。
步骤 4:验证配置描述文件中的证书
# Get certificate SHA-1 from keychain
security find-identity -v -p codesigning | grep "Apple Distribution"
# Output: 1) ABCDEF123... "Apple Distribution: Company (TEAMID)"
# Check if that certificate is embedded in the profile
security cms -D -i embedded.mobileprovision | plutil -extract DeveloperCertificates xml1 -o - -
# Decode one of the base64 certificates:
echo "<base64 data>" | base64 -d | openssl x509 -inform DER -noout -fingerprint -sha1
配置描述文件中的 SHA-1 必须与 find-identity 中的 SHA-1 匹配。
决策树
树 1:“未找到签名证书”
digraph tree1 {
"No signing certificate?" [shape=diamond];
"Run find-identity" [shape=box, label="security find-identity -v -p codesigning"];
"0 identities?" [shape=diamond];
"Has identities but wrong type?" [shape=diamond];
"Certificate expired?" [shape=diamond];
"Import certificate" [shape=box, label="Import .p12 into keychain\nsecurity import cert.p12 -k login.keychain-db -P pass -T /usr/bin/codesign"];
"Download from portal" [shape=box, label="Download certificate from\nApple Developer Portal\nor use Xcode > Preferences > Accounts"];
"Use correct cert type" [shape=box, label="Archive needs Apple Distribution\nDebug needs Apple Development\nCheck CODE_SIGN_IDENTITY"];
"Renew certificate" [shape=box, label="Revoke expired cert in portal\nCreate new cert\nUpdate profiles to use new cert"];
"CI keychain issue" [shape=box, label="In CI: create keychain, import cert,\nunlock, set-key-partition-list\n(see code-signing-ref CI section)"];
"No signing certificate?" -> "Run find-identity";
"Run find-identity" -> "0 identities?" [label="check output"];
"0 identities?" -> "Import certificate" [label="yes, no certs at all"];
"0 identities?" -> "Has identities but wrong type?" [label="no, has some"];
"Has identities but wrong type?" -> "Use correct cert type" [label="yes, dev only but need dist"];
"Has identities but wrong type?" -> "Certificate expired?" [label="no, correct type exists"];
"Certificate expired?" -> "Renew certificate" [label="yes"];
"Certificate expired?" -> "CI keychain issue" [label="no, cert valid but CI fails"];
"Import certificate" -> "Download from portal" [label="don't have .p12"];
}
树 2:“配置描述文件不包含签名证书”
digraph tree2 {
"Profile cert mismatch?" [shape=diamond];
"Automatic signing?" [shape=diamond];
"Clean and retry" [shape=box, label="Xcode > Preferences > Accounts\n> Download Manual Profiles\nClean build folder (Cmd+Shift+K)"];
"Regenerate profile" [shape=box, label="Apple Developer Portal:\n1. Edit profile\n2. Select current certificate\n3. Generate\n4. Download and install"];
"Check cert match" [shape=box, label="Step 4: Verify certificate SHA-1\nin keychain matches SHA-1\nembedded in profile"];
"Revoked cert" [shape=box, label="If someone regenerated the cert,\nall existing profiles are invalid.\nRegenerate profiles with new cert."];
"Profile cert mismatch?" -> "Automatic signing?" [label="check Xcode"];
"Automatic signing?" -> "Clean and retry" [label="yes"];
"Automatic signing?" -> "Check cert match" [label="no, manual signing"];
"Check cert match" -> "Regenerate profile" [label="SHA-1 mismatch"];
"Check cert match" -> "Revoked cert" [label="cert not in profile at all"];
}
树 3:ITMS-90035/90161 签名/配置描述文件无效
digraph tree3 {
"Upload rejected?" [shape=diamond];
"ITMS-90035?" [shape=diamond];
"ITMS-90161?" [shape=diamond];
"ITMS-90046?" [shape=diamond];
"Wrong cert" [shape=box, label="Signed with Development cert.\nRe-archive with Apple Distribution.\nCODE_SIGN_IDENTITY = Apple Distribution"];
"Cert expired" [shape=box, label="Certificate expired between\narchive and upload.\nRenew cert, re-archive."];
"Profile expired" [shape=box, label="Provisioning profile expired.\nRegenerate in portal,\nre-archive."];
"Profile mismatch" [shape=box, label="Profile doesn't match binary.\nCheck bundle ID alignment.\nVerify ExportOptions.plist."];
"Check entitlements" [shape=box, label="Entitlement not in profile.\nAdd capability in portal,\nregenerate profile, re-archive."];
"Upload rejected?" -> "ITMS-90035?" [label="check error code"];
"Upload rejected?" -> "ITMS-90046?" [label="ITMS-90046"];
"ITMS-90035?" -> "Wrong cert" [label="'Invalid Signature'"];
"ITMS-90035?" -> "ITMS-90161?" [label="different error"];
"ITMS-90046?" -> "Check entitlements" [label="'Invalid Entitlements'"];
"ITMS-90161?" -> "Profile expired" [label="'Invalid Provisioning Profile'"];
"ITMS-90161?" -> "Profile mismatch" [label="'profile doesn't match'"];
"Wrong cert" -> "Cert expired" [label="cert IS Distribution but still fails"];
}
树 4:CI 中出现 errSecInternalComponent / 钥匙串已锁定
digraph tree4 {
"errSecInternalComponent?" [shape=diamond];
"Keychain created?" [shape=diamond];
"Keychain unlocked?" [shape=diamond];
"Partition list set?" [shape=diamond];
"Search list correct?" [shape=diamond];
"Create keychain" [shape=box, label="security create-keychain -p pass build.keychain"];
"Unlock keychain" [shape=box, label="security unlock-keychain -p pass build.keychain"];
"Set partition list" [shape=box, label="security set-key-partition-list\n-S apple-tool:,apple: -s\n-k pass build.keychain\n(MOST COMMON FIX)"];
"Add to search list" [shape=box, label="security list-keychains -d user\n-s build.keychain login.keychain-db"];
"Set timeout" [shape=box, label="security set-keychain-settings\n-t 3600 -l build.keychain\n(prevent lock during long builds)"];
"Check runner image" [shape=box, label="Runner image may have changed.\nCheck GitHub Actions runner changelog.\nmacOS updates change keychain defaults."];
"errSecInternalComponent?" -> "Keychain created?" [label="CI environment"];
"Keychain created?" -> "Create keychain" [label="no"];
"Keychain created?" -> "Keychain unlocked?" [label="yes"];
"Keychain unlocked?" -> "Unlock keychain" [label="no"];
"Keychain unlocked?" -> "Partition list set?" [label="yes"];
"Partition list set?" -> "Set partition list" [label="no — this is the #1 fix"];
"Partition list set?" -> "Search list correct?" [label="yes"];
"Search list correct?" -> "Add to search list" [label="no — keychain not in search path"];
"Search list correct?" -> "Set timeout" [label="yes — try extending timeout"];
"Set timeout" -> "Check runner image" [label="still failing"];
}
树 5:身份不明确 / 存在多个证书
digraph tree5 {
"Ambiguous identity?" [shape=diamond];
"Same name, different dates?" [shape=diamond];
"Dev and Dist both present?" [shape=diamond];
"List all" [shape=box, label="security find-identity -v -p codesigning\nNote SHA-1 hashes and expiry dates"];
"Delete expired" [shape=box, label="Open Keychain Access\nDelete expired certificate\n(check expiry with openssl x509 -enddate)"];
"Use SHA-1" [shape=box, label="Specify exact identity by SHA-1:\nCODE_SIGN_IDENTITY = 'SHA1HASH'\nor codesign -s 'SHA1HASH'"];
"Specify full name" [shape=box, label="Use full identity name:\nCODE_SIGN_IDENTITY =\n'Apple Distribution: Company (TEAMID)'"];
"Ambiguous identity?" -> "List all" [label="first"];
"List all" -> "Same name, different dates?" [label="inspect"];
"Same name, different dates?" -> "Delete expired" [label="yes, old + new cert"];
"Same name, different dates?" -> "Dev and Dist both present?" [label="no"];
"Dev and Dist both present?" -> "Specify full name" [label="yes, Xcode picks wrong one"];
"Dev and Dist both present?" -> "Use SHA-1" [label="still ambiguous"];
}
树 6:授权不匹配 / 缺少功能
digraph tree6 {
"Entitlement error?" [shape=diamond];
"In Xcode but not profile?" [shape=diamond];
"In profile but not Xcode?" [shape=diamond];
"Run Step 3" [shape=box, label="Compare entitlements:\n1. codesign -d --entitlements - App\n2. Profile entitlements\n3. .entitlements file"];
"Regenerate profile" [shape=box, label="Apple Developer Portal:\n1. App ID > Capabilities\n2. Enable missing capability\n3. Edit profile\n4. Generate and download"];
"Add capability" [shape=box, label="Xcode > Target >\nSigning & Capabilities >\n+ Capability"];
"Remove stale entitlement" [shape=box, label="Remove capability from\n.entitlements file that\nisn't supported by profile type"];
"Entitlement error?" -> "Run Step 3" [label="diagnose"];
"Run Step 3" -> "In Xcode but not profile?" [label="compare"];
"In Xcode but not profile?" -> "Regenerate profile" [label="yes, capability missing from profile"];
"In Xcode but not profile?" -> "In profile but not Xcode?" [label="no"];
"In profile but not Xcode?" -> "Add capability" [label="yes"];
"In profile but not Xcode?" -> "Remove stale entitlement" [label="entitlement not valid for profile type"];
}
快速参考表
| 症状 | 检查 | 修复 |
|---|---|---|
| 未找到签名证书 | security find-identity -v -p codesigning | 导入证书或从门户网站下载 |
| 描述文件未包含证书 | 步骤 4:SHA-1 比较 | 使用当前证书重新生成描述文件 |
| ITMS-90035 签名无效 | 对归档应用运行 codesign -dv | 使用 Apple Distribution 证书重新归档 |
| ITMS-90161 描述文件无效 | 对描述文件运行 security cms -D -i | 重新生成未过期的描述文件 |
| ITMS-90046 权限无效 | 步骤 3:三方比较 | 在门户网站中添加功能,然后重新生成描述文件 |
| errSecInternalComponent | CI 钥匙串设置 | set-key-partition-list(最常见的修复方法) |
| 身份不明确 | security find-identity -v 的计数 | 删除过期证书或使用 SHA-1 哈希值 |
| 权限不匹配 | 三方权限比较 | 使 Xcode、描述文件和 .entitlements 保持一致 |
| 描述文件已过期 | 使用 security cms -D 检查 ExpirationDate | 从门户网站下载新的描述文件 |
| 描述文件缺少推送权限 | 在描述文件中运行 grep aps-environment | 在门户网站中启用 Push,然后重新生成描述文件 |
| 扩展签名失败 | 扩展目标的签名配置 | 每个扩展都需要自己的描述文件,且团队必须匹配 |
| 本地运行正常,CI 失败 | CI 钥匙串脚本是否完整 | 完整设置:创建、解锁、导入、分区列表、搜索列表 |
| “codesign 想要访问密钥” | 钥匙串访问设置 | security set-key-partition-list 或“钥匙串访问” >“显示简介”>“访问控制” |
| App Groups 权限错误 | 三方比较 | 在门户网站的 App ID 中添加 App Group,然后重新生成描述文件 |
| 构建成功,导出失败 | ExportOptions.plist | 验证 plist 中的方法、描述文件名称和团队 ID |
压力场景
场景 1:“直接全部重新生成”
背景:代码签名失败。团队成员建议撤销所有证书并重新生成,以便从头开始。
压力:“全部重新生成只要 5 分钟。”
实际情况:撤销分发证书会使所有使用该证书的描述文件失效,影响所有团队成员和 CI 系统。每位开发者都需要新证书。每条 CI 流水线都会中断。每个描述文件都需要重新生成。这个“5 分钟修复”会变成全团队停工 2–4 小时。
正确做法:按照步骤 1–4 诊断具体问题。大多数签名故障都是某个单独组件过期或不匹配,而不是系统性问题。
回应模板:“撤销证书会导致整个团队和所有 CI 流水线都无法签名。先让我按步骤进行诊断——90% 的签名问题都是单个证书过期或描述文件不匹配,可以在 5 分钟内修复,而且不会影响其他人。”
场景 2:“Xcode 更新后签名出问题了——回滚”
背景:更新 Xcode 后,代码签名无法正常工作。有人建议回滚 Xcode。
压力:“更新前构建还能正常运行,所以就是这次更新导致的。”
实际情况:Xcode 更新有时会使托管签名缓存失效,或改变自动签名选择描述文件的方式。但证书和描述文件本身并没有改变。回滚 Xcode 会让你无法使用新的 SDK 功能,也无法修复根本的配置问题。
正确做法:运行步骤 1–2,确认证书和描述文件仍然有效。检查 Xcode 的自动签名是否选用了不同的描述文件。尝试:Xcode → Preferences → Accounts → Download Manual Profiles。清理构建文件夹。
回应模板:“Xcode 更新不会改变我们的证书或描述文件。先让我检查 Xcode 的自动签名现在选中了什么——它很可能选了一个与之前不同的描述文件。花 5 分钟检查,就能准确找出变更所在。”
场景 3:“归档失败了,让我重新归档——可能是归档损坏了”
背景:归档成功,但导出或上传失败。开发者认为归档已损坏,想重新归档。
压力:“再试一次吧,这次可能就行了。”
实际情况:归档“损坏”的情况极为罕见。导出失败几乎总是因为:ExportOptions.plist 中的方法错误、归档使用的证书类型错误,或配置文件不匹配。使用相同设置重新归档只会得到相同结果。
正确做法:重新构建前先检查归档:
- 对 .xcarchive 中的 .app 执行
codesign -dv,查看签名者 - 检查 ExportOptions.plist 中的方法是否符合预期(app-store、ad-hoc 等)
- 确认 ExportOptions 指定的配置文件存在且未过期
回应模板:“使用相同设置重新归档只会得到相同结果。我先检查归档中的实际内容——用 codesign -dv 只需 30 秒,就能准确告诉我们导出失败的原因。”
检查清单
宣布签名问题已修复前:
-
security find-identity -v -p codesigning显示预期的身份 - 使用
security cms -D解码配置文件——未过期,且包含正确的证书 - 三方权限比较结果一致(二进制文件、配置文件、.entitlements 文件)
- 使用正确的
CODE_SIGN_IDENTITY成功构建/归档 - 如果使用 CI:已创建并解锁钥匙串、已设置分区列表、已导入证书
- 如果使用 CI:成功和失败时都会运行清理步骤
资源
WWDC:2021-10204、2022-110353
文档:/security、/bundleresources/entitlements、/xcode/distributing-your-app
技能:axiom-code-signing、axiom-code-signing-ref