mcpField
A
axiom-code-signing-diag

axiom-code-signing-diag

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

关于 axiom-code-signing-diag

面向 iOS/macOS 开发者的代码签名故障诊断技能,通过检查证书、描述文件、权限和 Keychain 配置来定位构建及上传问题,并提供按症状分类的排查路径。

主要功能

  1. 01检查签名身份
  2. 02解码描述文件
  3. 03比较应用权限配置
  4. 04排查 CI Keychain 故障
  5. 05按错误症状提供决策树

适用场景

  1. 01诊断找不到签名证书
  2. 02排查 CI 中的签名失败
  3. 03定位权限或描述文件不匹配
  4. 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 上传被拒。

概述

核心原则:代码签名失败时,问题通常出在:

  1. 证书问题(已过期、缺失、类型错误、已吊销)— 30%
  2. 描述文件问题(已过期、缺少证书、App ID 错误、缺少功能)— 25%
  3. 权限不匹配(Xcode 中启用了某项功能,但描述文件中没有,反之亦然)— 15%
  4. 钥匙串问题(CI 中已锁定、errSecInternalComponent、分区列表)— 15%
  5. 归档/导出问题(导出方法错误、分发时证书类型错误)— 10%
  6. 签名身份不明确(存在多个匹配的证书,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 archiveExportOptions.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:三方比较在门户网站中添加功能,然后重新生成描述文件
errSecInternalComponentCI 钥匙串设置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 中的方法错误、归档使用的证书类型错误,或配置文件不匹配。使用相同设置重新归档只会得到相同结果。

正确做法:重新构建前先检查归档:

  1. 对 .xcarchive 中的 .app 执行 codesign -dv,查看签名者
  2. 检查 ExportOptions.plist 中的方法是否符合预期(app-store、ad-hoc 等)
  3. 确认 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

评论

支持 Markdown