PAYATHON 2026

Xcode Server 构建出现多个匹配的 codesigning identities

排障赵师傅

结论

重复的 signing identities 通常保存在 Xcode Server 自己维护的 keychain 中,不会出现在登录用户、_xcsbuildd 或 _xcsd 的默认 keychain 搜索列表里。所以下面的命令查不到它们是正常的:

security find-identity -p codesigning
sudo -u _xcsbuildd security find-identity -p codesigning
sudo -u _xcsd security find-identity -p codesigning

需要显式指定 /Library/Developer/XcodeServer/Keychains 下的 keychain 文件,找到同名 identity,删除已经过期的项,然后让 Server.app 重新同步 Developer Team。

不要直接删除整个 Certificates 或 Keychains 目录。这些目录可能还保存着 Xcode Server 的私钥和内部证书数据,整体删除可能导致所有 bot 的签名配置失效。

删除 provisioning profiles 为什么没用

错误信息已经指出,重复匹配的是:

certificate and private key pairs

这指的是 codesigning identity。每个 identity 由证书及其对应的私钥组成。provisioning profile 只规定可以参与签名的证书、App ID 和权限。

删除 provisioning profiles 只会清理描述文件,不会删除 keychain 中的证书或私钥。只要同名的新旧证书仍然同时存在,Xcode 就会继续报告多个匹配项。

旧证书到期后,Xcode Server 可能重新申请或导入了一份同名证书,却没有清除原来的 identity。这可能解释问题为什么正好出现在旧内容过期之后。具体是否由自动更新引起,不能只凭过期日期判断,应以 keychain 中实际列出的 identity 为准。

查找 Xcode Server 使用的 keychain

先列出 Xcode Server 目录中的 keychain 文件:

sudo find /Library/Developer/XcodeServer/Keychains \
  -type f \
  \( -name '*.keychain' -o -name '*.keychain-db' \) \
  -print

Yosemite 时代通常使用 .keychain 文件,但文件名不一定固定,应以命令输出为准。

对找到的每个文件显式执行查询,例如:

sudo security find-identity -v -p codesigning \
  "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain"

也可以按证书名称查询:

sudo security find-certificate -a \
  -c "Mac Developer: OS X Server" \
  -Z \
  "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain"

需要把 keychain 路径作为最后一个参数传入。仅切换执行用户,不会自动将 Xcode Server 的私有 keychain 加入该用户的搜索列表。

如果有重复项,find-identity 通常会列出两个或更多 SHA-1 标识,其名称相同或相近。

核对证书有效期

删除前要先确认哪一项已经过期。可以导出匹配证书的 PEM 数据:

sudo security find-certificate -a \
  -c "Mac Developer: OS X Server" \
  -p \
  "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain" \
  > /tmp/xcode-server-certificates.pem

如果只导出了一张证书,可以直接查看:

openssl x509 \
  -in /tmp/xcode-server-certificates.pem \
  -noout \
  -subject \
  -issuer \
  -serial \
  -dates \
  -fingerprint \
  -sha1

如果文件中包含多张连续的 PEM 证书,需要拆开逐张检查。也可以临时把该 keychain 添加到 Keychain Access 中查看,但不要修改其他系统 keychain。

检查时记录这些信息:

  • find-identity 输出的 SHA-1
  • 证书名称和 Team ID
  • notBefore 与 notAfter
  • 已经过期和仍然有效的项

如果无法确认私钥和证书的对应关系,先不要删除。

备份并停止 Xcode Server

修改前先备份相关目录:

sudo ditto \
  "/Library/Developer/XcodeServer/Keychains" \
  "/Library/Developer/XcodeServer/Keychains.backup"

sudo ditto \
  "/Library/Developer/XcodeServer/Certificates" \
  "/Library/Developer/XcodeServer/Certificates.backup"

备份目标目录应当不存在,以免新旧文件混在一起。备份中可能包含私钥,因此只能允许管理员访问。

然后停止 Xcode 服务:

sudo serveradmin stop xcode

如果当前 Server.app 版本不接受这个服务名,请直接在 Server.app 中停止 Xcode 服务。不同 Server 版本使用的命令行服务名称可能不同。

删除过期的 identity

使用 find-identity 查到的 SHA-1 删除过期 identity:

sudo security delete-identity \
  -Z "过期IDENTITY的SHA1" \
  "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain"

例如:

sudo security delete-identity \
  -Z "0123456789ABCDEFFEDCBA987654321001234567" \
  "/Library/Developer/XcodeServer/Keychains/xcs.keychain"

只能删除已经确认过期或不再使用的 identity。delete-identity 会同时处理证书及其私钥,误删仍在使用的项会导致签名完全失败。

删除后再次检查:

sudo security find-identity -v -p codesigning \
  "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain"

对应名称应当只剩一个有效匹配项。

重新同步 Developer Team

清理完成后启动服务:

sudo serveradmin start xcode

随后在 Server.app 中完成以下操作:

  1. 确认旧的 Developer Team 已经移除。
  2. 重新添加开发者账号和 Team。
  3. 等待 provisioning profiles 和证书同步完成。
  4. 再次检查 Xcode Server 的 keychain,确认没有重新生成重复 identity。
  5. 先运行启用了 Perform analyze action 的 bot,再验证 archive。

如果刚删除的过期 identity 再次出现,它可能是从 /Library/Developer/XcodeServer/Certificates 中的旧数据重新导入的。不要批量删除这个目录。先停止服务并确认已有备份,然后逐个检查其中的证书文件,只移走已经确认过期且与重复 SHA-1 对应的文件,最后重新启动服务。

命令无法访问 keychain 时

可能会遇到以下错误:

User interaction is not allowed

或者:

The specified keychain could not be found

先检查路径和权限:

sudo ls -la "/Library/Developer/XcodeServer/Keychains"
sudo file "/Library/Developer/XcodeServer/Keychains/实际文件名.keychain"

Xcode Server 的内部 keychain 可能使用了由服务管理的密码或访问控制设置。如果无法解锁,不要尝试替换权限、修改 owner 或直接删除 keychain 文件,否则可能破坏 Xcode Server 保存的全部签名凭据。

最后的恢复手段

旧版 Xcode Server 提供过重置服务数据的工具,部分安装环境可以使用:

sudo xcrun xcscontrol --reset

该操作可能清除 bots、integrations、repositories、凭据和其他 Xcode Server 数据。不同 Xcode/Server 版本的清理范围可能不同,执行前先查看本机帮助:

sudo xcrun xcscontrol --help

只有在服务器已经完成备份、bots 可以重建,并且定向删除过期 identity 仍然无效时,才考虑完整重置。

关闭 Perform analyze action 或 Perform archive action 只能绕开需要签名的构建路径,无法解决 identity 重复的问题。应当直接检查 Xcode Server 的私有 keychain,保留一个有效 identity,并删除已经过期的重复项。

备注:内容仅供参考。