axiom-code-signing-diag
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCode Signing Diagnostics
代码签名诊断
Systematic troubleshooting for code signing failures: missing certificates, provisioning profile mismatches, Keychain issues in CI, entitlement conflicts, and App Store upload rejections.
针对代码签名失败的系统性排查方案:包括证书缺失、配置文件不匹配、CI环境中的钥匙串问题、权限冲突以及App Store上传被拒等场景。
Overview
概述
Core Principle: When code signing fails, the problem is usually:
- Certificate issues (expired, missing, wrong type, revoked) — 30%
- Provisioning profile issues (expired, missing cert, wrong App ID, missing capability) — 25%
- Entitlement mismatches (capability in Xcode but not in profile, or vice versa) — 15%
- Keychain issues (locked in CI, errSecInternalComponent, partition list) — 15%
- Archive/export issues (wrong export method, wrong cert type for distribution) — 10%
- Ambiguous identity (multiple matching certificates, Xcode picks wrong one) — 5%
Always verify certificate + profile + entitlements BEFORE rewriting build settings or regenerating everything.
核心原则:代码签名失败时,问题通常分为以下几类:
- 证书问题(过期、缺失、类型错误、被吊销)——占比30%
- 配置文件问题(过期、缺少证书、App ID错误、缺少功能权限)——占比25%
- 权限不匹配(Xcode中配置了功能但配置文件中没有,反之亦然)——占比15%
- 钥匙串问题(CI环境中钥匙串锁定、errSecInternalComponent错误、分区列表配置)——占比15%
- 归档/导出问题(导出方式错误、分发使用了错误的证书类型)——占比10%
- 身份模糊(存在多个匹配证书,Xcode选择了错误的证书)——占比5%
在修改构建设置或重新生成所有内容之前,务必先验证证书、配置文件和权限。
Red Flags
预警信号
Symptoms that indicate code signing–specific issues:
| Symptom | Likely Cause |
|---|---|
| "No signing certificate found" | Certificate expired, revoked, or not in keychain |
| "Provisioning profile doesn't include signing certificate" | Profile generated with different cert than the one in keychain |
| ITMS-90035 Invalid Signature | Signed with Development cert instead of Distribution |
| ITMS-90161 Invalid Provisioning Profile | Profile expired or doesn't match binary |
| errSecInternalComponent in CI | Keychain locked or |
| "Ambiguous — matches multiple" | Multiple valid certs with same name (dev + expired) |
| "Entitlement not allowed by profile" | Capability added in Xcode but profile not regenerated |
| "codesign wants to access key" dialog | Keychain access not granted to codesign |
| Build works locally, fails in CI | Missing keychain setup steps (create, unlock, partition list) |
| "Profile doesn't match bundle ID" | Bundle identifier mismatch between Xcode target and profile |
| Export fails after successful archive | ExportOptions.plist specifies wrong method or profile |
| App extension signing fails | Extension needs its own profile with matching team and prefix |
表明存在代码签名特定问题的症状:
| 症状 | 可能原因 |
|---|---|
| "未找到签名证书" | 证书过期、被吊销或未在钥匙串中 |
| "配置文件不包含签名证书" | 配置文件生成时使用的证书与钥匙串中的证书不一致 |
| ITMS-90035 无效签名 | 使用了Development证书而非Distribution证书签名 |
| ITMS-90161 无效配置文件 | 配置文件过期或与二进制文件不匹配 |
| CI环境中出现errSecInternalComponent | 钥匙串锁定或未调用 |
| "模糊匹配——存在多个匹配项" | 存在多个同名的有效证书(如开发证书和过期证书) |
| "权限不被配置文件允许" | Xcode中添加了功能但未重新生成配置文件 |
| "codesign想要访问密钥"对话框 | 未授予codesign访问钥匙串的权限 |
| 本地构建正常,CI构建失败 | 缺少钥匙串设置步骤(创建、解锁、分区列表配置) |
| "配置文件与Bundle ID不匹配" | Xcode目标的Bundle标识符与配置文件不匹配 |
| 归档成功但导出失败 | ExportOptions.plist指定了错误的导出方式或配置文件 |
| App扩展签名失败 | 扩展需要独立的配置文件,且团队和前缀需匹配 |
Anti-Rationalization
误区规避
| Rationalization | Why It Fails | Time Cost |
|---|---|---|
| "Certificate was fine yesterday" | Certificates expire and get revoked. Profiles auto-regenerate in portal changes. Always re-verify. | 30-60 min debugging build settings when cert expired overnight |
| "Let me regenerate everything" | Regenerating certificates revokes the old ones, breaking other team members and CI. Diagnose first. | 2-4 hours + broken teammates + CI pipeline down |
| "I'll reset my keychain" | Destroys ALL stored credentials (SSH keys, saved passwords, other certs). Diagnose the specific cert. | 1-2 hours restoring all credentials |
| "Just disable code signing for now" | Code signing can't be disabled for device builds or distribution. You'll hit the same issue later with less time. | Wasted time plus the original problem remains |
| "It's an Xcode bug, let me reinstall" | Code signing is configuration, not an Xcode bug. Reinstalling doesn't change your certificates or profiles. | 2-4 hours reinstalling Xcode while the config stays broken |
| "I'll use the team provisioning profile" | Xcode's auto-managed wildcard profile lacks specific entitlements (push, App Groups). It won't work for apps needing capabilities. | 30+ min discovering missing capabilities |
| "CI worked before, nothing changed on our side" | Apple revokes certificates for security reasons. CI runner macOS updates change keychain behavior. Provisioning profiles expire after 1 year. | Hours of "but we didn't change anything" while the cert is expired |
| "Let me check the code first" | Code signing errors are NEVER code bugs. They are 100% configuration — certificates, profiles, entitlements, and keychains. | Hours debugging working code while the profile is expired |
| "Set build.keychain as default" | | 30+ min restoring default keychain + mysterious SSH/credential failures |
| 错误思路 | 为何行不通 | 时间成本 |
|---|---|---|
| "昨天证书还能用" | 证书会过期或被吊销,配置文件会在后台自动重新生成。务必重新验证。 | 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设为默认" | | 30分钟以上恢复默认钥匙串,加上神秘的SSH/凭据访问失败 |
Mandatory First Steps
必做第一步
Before changing build settings or regenerating certificates, run these diagnostics:
在修改构建设置或重新生成证书之前,执行以下诊断步骤:
Step 1: Check Signing Identities
步骤1:检查签名身份
bash
security find-identity -v -p codesigningExpected output:
- At least one valid identity with "Apple Development" or "Apple Distribution"
- Each shows SHA-1 hash + name + Team ID
Problems:
- 0 valid identities → No certificates installed or all expired
- Only "Apple Development" but trying to archive → Need Distribution certificate
- Multiple entries with same name → Ambiguous identity (see Tree 5)
bash
security find-identity -v -p codesigning预期输出:
- 至少一个有效的“Apple Development”或“Apple Distribution”身份
- 每个身份显示SHA-1哈希值、名称和团队ID
常见问题:
- 0个有效身份 → 未安装证书或所有证书已过期
- 只有“Apple Development”身份但尝试归档 → 需要Distribution证书
- 存在多个同名身份 → 身份模糊(见树形图5)
Step 2: Decode Provisioning Profile
步骤2:解码配置文件
bash
undefinedbash
undefinedFind the profile being used
找到正在使用的配置文件
find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -3
find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -3
Decode it
解码配置文件
security cms -D -i path/to/embedded.mobileprovision
**Check these fields**:
- `ExpirationDate` → Not expired?
- `DeveloperCertificates` → Contains your current certificate?
- `Entitlements` → Contains all capabilities your app uses?
- `ProvisionedDevices` → Contains your test device UDID? (Development/Ad Hoc only)
- `Name` → Matches what Xcode is configured to use?security cms -D -i path/to/embedded.mobileprovision
**需要检查的字段**:
- `ExpirationDate` → 是否过期?
- `DeveloperCertificates` → 是否包含当前证书?
- `Entitlements` → 是否包含应用使用的所有功能权限?
- `ProvisionedDevices` → 是否包含测试设备的UDID?(仅Development/Ad Hoc配置文件)
- `Name` → 是否与Xcode中配置的一致?Step 3: Extract and Compare Entitlements
步骤3:提取并对比权限
bash
undefinedbash
undefinedWhat entitlements does the built app have?
已构建应用的权限是什么?
codesign -d --entitlements - /path/to/MyApp.app
codesign -d --entitlements - /path/to/MyApp.app
What entitlements does the profile grant?
配置文件授予的权限是什么?
security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -
security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -
What entitlements does Xcode's .entitlements file declare?
Xcode的.entitlements文件中声明的权限是什么?
cat MyApp/MyApp.entitlements
**All three must agree.** Any mismatch → signing failure.cat MyApp/MyApp.entitlements
**这三者必须完全一致**。任何不匹配都会导致签名失败。Step 4: Verify Certificate in Profile
步骤4:验证配置文件中的证书
bash
undefinedbash
undefinedGet certificate SHA-1 from keychain
从钥匙串获取证书SHA-1
security find-identity -v -p codesigning | grep "Apple Distribution"
security find-identity -v -p codesigning | grep "Apple Distribution"
Output: 1) ABCDEF123... "Apple Distribution: Company (TEAMID)"
输出示例: 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 - -
security cms -D -i embedded.mobileprovision | plutil -extract DeveloperCertificates xml1 -o - -
Decode one of the base64 certificates:
解码其中一个base64证书:
echo "<base64 data>" | base64 -d | openssl x509 -inform DER -noout -fingerprint -sha1
The SHA-1 from the profile must match the SHA-1 from `find-identity`.echo "<base64 data>" | base64 -d | openssl x509 -inform DER -noout -fingerprint -sha1
配置文件中的SHA-1必须与`find-identity`输出的SHA-1匹配。Decision Trees
诊断树形图
Tree 1: "No signing certificate found"
树形图1:“未找到签名证书”
dot
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"];
}dot
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"];
}Tree 2: "Provisioning profile doesn't include signing certificate"
树形图2:“配置文件不包含签名证书”
dot
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"];
}dot
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"];
}Tree 3: ITMS-90035/90161 Invalid Signature/Profile
树形图3:ITMS-90035/90161 无效签名/配置文件
dot
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"];
}dot
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"];
}Tree 4: errSecInternalComponent / Keychain Locked in CI
树形图4:errSecInternalComponent / CI环境钥匙串锁定
dot
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"];
}dot
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"];
}Tree 5: Ambiguous Identity / Multiple Certificates
树形图5:身份模糊 / 多个证书
dot
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"];
}dot
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"];
}Tree 6: Entitlement Mismatch / Missing Capability
树形图6:权限不匹配 / 缺少功能权限
dot
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"];
}dot
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"];
}Quick Reference Table
快速参考表
| Symptom | Check | Fix |
|---|---|---|
| No signing certificate found | | Import cert or download from portal |
| Provisioning profile doesn't include cert | Step 4: SHA-1 comparison | Regenerate profile with current cert |
| ITMS-90035 Invalid Signature | | Re-archive with Apple Distribution cert |
| ITMS-90161 Invalid Provisioning Profile | | Regenerate non-expired profile |
| ITMS-90046 Invalid Entitlements | Step 3: three-way comparison | Add capability in portal, regenerate profile |
| errSecInternalComponent | CI keychain setup | |
| Ambiguous identity | | Delete expired cert or use SHA-1 hash |
| Entitlement mismatch | Three-way entitlement comparison | Align Xcode, profile, and .entitlements |
| Profile expired | | Download fresh profile from portal |
| Profile missing push | | Enable Push in portal, regenerate profile |
| Extension signing fails | Extension target signing config | Each extension needs own profile with matching team |
| Works locally, fails CI | CI keychain script completeness | Full setup: create, unlock, import, partition list, search list |
| "codesign wants to access key" | Keychain access settings | |
| App Groups entitlement error | Three-way comparison | Add App Group in portal App ID, regenerate profile |
| Build works, export fails | ExportOptions.plist | Verify method, profile name, team ID in plist |
| 症状 | 检查项 | 修复方案 |
|---|---|---|
| 未找到签名证书 | | 导入证书或从开发者门户下载 |
| 配置文件不包含证书 | 步骤4:SHA-1对比 | 使用当前证书重新生成配置文件 |
| ITMS-90035无效签名 | 对归档应用执行 | 使用Apple Distribution证书重新归档 |
| ITMS-90161无效配置文件 | 对配置文件执行 | 重新生成未过期的配置文件 |
| ITMS-90046无效权限 | 步骤3:三方权限对比 | 在开发者门户添加功能,重新生成配置文件 |
| errSecInternalComponent | CI钥匙串设置 | 执行 |
| 身份模糊 | | 删除过期证书或使用SHA-1哈希值 |
| 权限不匹配 | 三方权限对比 | 对齐Xcode、配置文件和.entitlements文件 |
| 配置文件过期 | | 从开发者门户下载最新配置文件 |
| 配置文件缺少推送权限 | 在配置文件中执行 | 在开发者门户启用推送,重新生成配置文件 |
| 扩展签名失败 | 扩展目标的签名配置 | 每个扩展需要独立的配置文件,且团队信息匹配 |
| 本地正常CI失败 | CI钥匙串脚本完整性 | 完整设置:创建、解锁、导入、分区列表、搜索列表 |
| "codesign想要访问密钥"对话框 | 钥匙串访问设置 | 执行 |
| App Groups权限错误 | 三方权限对比 | 在开发者门户的App ID中添加App Group,重新生成配置文件 |
| 构建正常导出失败 | ExportOptions.plist | 验证导出方式、配置文件名称、团队ID是否正确 |
Pressure Scenarios
压力场景应对
Scenario 1: "Just regenerate everything"
场景1:“直接重新生成所有内容就行了”
Context: Code signing fails. Team member suggests revoking all certificates and generating new ones to start fresh.
Pressure: "It'll only take 5 minutes to regenerate everything."
Reality: Revoking a distribution certificate invalidates ALL provisioning profiles that use it — across ALL team members and CI systems. Every developer needs new certificates. Every CI pipeline breaks. Every profile needs regeneration. The "5 minute fix" becomes a 2-4 hour team-wide outage.
Correct action: Diagnose the specific issue with Steps 1-4. Most signing failures are a single expired or mismatched component, not a systemic problem.
Push-back template: "Revoking certificates breaks signing for everyone on the team and all CI pipelines. Let me run the diagnostic steps first — 90% of signing issues are a single expired cert or mismatched profile, fixable in 5 minutes without affecting anyone else."
背景:代码签名失败。团队成员建议吊销所有证书并生成新证书,从头开始。
压力:“重新生成只需要5分钟。”
实际情况:吊销分发证书会使所有使用该证书的配置文件失效——影响所有团队成员和CI系统。每个开发者都需要新证书,每个CI流水线都会中断。“5分钟修复”会变成2-4小时的团队范围故障。
正确操作:使用步骤1-4诊断具体问题。大多数签名失败只是单个过期或不匹配的组件,无需影响他人即可在5分钟内修复。
反驳话术:“吊销证书会导致团队所有人和CI流水线的签名失效。我先执行诊断步骤——90%的签名问题都是单个过期证书或不匹配的配置文件,5分钟就能修复,不会影响任何人。”
Scenario 2: "Xcode updated, signing broke — rollback"
场景2:“Xcode更新后签名失效——回滚Xcode”
Context: After an Xcode update, code signing stopped working. Someone suggests rolling back Xcode.
Pressure: "The build worked before the update, so the update broke it."
Reality: Xcode updates sometimes invalidate managed signing caches or change how automatic signing selects profiles. But the certificates and profiles themselves don't change. Rolling back Xcode loses access to new SDK features and doesn't fix the underlying configuration.
Correct action: Run Steps 1-2 to verify certificates and profiles are still valid. Check if Xcode's automatic signing is selecting a different profile. Try: Xcode → Preferences → Accounts → Download Manual Profiles. Clean build folder.
Push-back template: "Xcode updates don't change our certificates or profiles. Let me check what Xcode's automatic signing is selecting now — it's likely picking a different profile than before. A 5-minute check will tell us exactly what changed."
背景:Xcode更新后,代码签名停止工作。有人建议回滚Xcode版本。
压力:“更新前构建正常,肯定是更新搞坏了。”
实际情况:Xcode更新有时会使自动签名的缓存失效,或改变自动签名选择配置文件的逻辑,但证书和配置文件本身不会改变。回滚Xcode会失去新SDK功能的访问权限,且无法修复底层配置问题。
正确操作:执行步骤1-2验证证书和配置文件是否仍有效。检查Xcode的自动签名是否选择了不同的配置文件。尝试:Xcode → 偏好设置 → 账户 → 下载手动配置文件。清理构建文件夹。
反驳话术:“Xcode更新不会改变我们的证书或配置文件。我先检查Xcode自动签名现在选择的是哪个配置文件——很可能是选了一个不同的配置文件。5分钟的检查就能明确变化点。”
Scenario 3: "The archive failed, let me re-archive — it's probably corruption"
场景3:“归档失败了,我重新归档——可能是损坏了”
Context: Archive succeeded but export or upload failed. Developer wants to re-archive assuming the archive was corrupted.
Pressure: "Just do it again, it'll probably work this time."
Reality: Archive "corruption" is extremely rare. Export failures are almost always: wrong ExportOptions.plist method, wrong certificate type in the archive, or profile mismatch. Re-archiving with the same settings produces the same result.
Correct action: Inspect the archive before re-building:
- on the .app inside the .xcarchive to see what signed it
codesign -dv - Check ExportOptions.plist method matches intent (app-store, ad-hoc, etc.)
- Verify the profile specified in ExportOptions exists and isn't expired
Push-back template: "Re-archiving with the same settings will produce the same result. Let me check what's actually in the archive — it takes 30 seconds with codesign -dv and will tell us exactly why export failed."
背景:归档成功但导出或上传失败。开发人员想重新归档,认为是归档损坏。
压力:“再试一次就行,这次应该能成功。”
实际情况:归档“损坏”极其罕见。导出失败几乎总是因为:ExportOptions.plist导出方式错误、归档中使用的证书类型错误、或配置文件不匹配。使用相同设置重新归档会得到相同结果。
正确操作:重新构建前先检查归档:
- 对.xcarchive中的.app执行,查看签名信息
codesign -dv - 检查ExportOptions.plist的导出方式是否符合预期(app-store、ad-hoc等)
- 验证ExportOptions中指定的配置文件是否存在且未过期
反驳话术:“使用相同设置重新归档会得到相同结果。我先检查归档的实际内容——用codesign -dv只需要30秒,就能明确导出失败的原因。”
Checklist
验证清单
Before declaring a signing issue fixed:
- shows the expected identity
security find-identity -v -p codesigning - Profile decoded with — not expired, contains correct cert
security cms -D - Three-way entitlement comparison agrees (binary, profile, .entitlements file)
- Build/archive succeeds with correct
CODE_SIGN_IDENTITY - If CI: keychain created, unlocked, partition list set, cert imported
- If CI: cleanup step runs on success AND failure
在确认签名问题已修复前,请检查:
- 显示预期的签名身份
security find-identity -v -p codesigning - 使用解码配置文件——未过期且包含正确证书
security cms -D - 三方权限对比一致(二进制文件、配置文件、.entitlements文件)
- 使用正确的完成构建/归档
CODE_SIGN_IDENTITY - 若为CI环境:已完成钥匙串创建、解锁、证书导入、分区列表设置
- 若为CI环境:成功和失败时都执行了清理步骤
Resources
参考资源
WWDC: 2021-10204, 2022-110353
Docs: /security, /bundleresources/entitlements, /xcode/distributing-your-app
Skills: axiom-code-signing, axiom-code-signing-ref
WWDC:2021-10204, 2022-110353
文档:/security, /bundleresources/entitlements, /xcode/distributing-your-app
技能:axiom-code-signing, axiom-code-signing-ref