macOS · 安全 · Shellby

让 macOS 不再每次连接都弹钥匙串授权——一次 Keychain 选型

Shellby 复盘:同一段存密码的代码,在 macOS 上每次连接都弹授权窗,换一种钥匙串就好了。

Shellby 把服务器密码和私钥存在系统钥匙串里。开发过程中撞到一个烦人的问题:在 macOS 上,每次连接服务器都会弹出一个「Shellby 想使用钥匙串中储存的机密信息」的授权窗,点一下才能继续。iOS 上没有,就 macOS 有。

这篇讲这个弹窗从哪来、以及换一种钥匙串怎么根治它——一个「同样的代码、换个存储后端就好」的小选型,但背后是 macOS 两套钥匙串模型的差异。

弹窗从哪来

macOS 上其实有两套钥匙串:

一套是传统的、基于文件的钥匙串(login.keychain 那一套)。它的访问授权是按二进制的代码签名 ACL 来判定的——某个 App 第一次访问某条钥匙串项时,你授权「总是允许」,系统把这个 App 的代码签名信息记进这条项的 ACL 里。以后同一个 App 来访问,签名对得上就放行。

问题就出在这里。开发期你反复重新编译、用的是临时签名,每编译一次,二进制的 cdhash(代码签名摘要)就变一次。cdhash 一变,ACL 里记的那个「可信 App」就对不上了——系统认为这是个「不一样的」程序在访问,于是又弹一次授权窗。你改一行代码、重跑、连接,弹窗;再改一行、再跑、再连,又弹窗。

换成数据保护钥匙串

修法不是去驯服传统钥匙串的 ACL,而是干脆换一套:数据保护钥匙串(data-protection keychain),也就是 iOS 上那套。

具体做法很小——所有钥匙串查询统一带上一个标志:

query[kSecUseDataProtectionKeychain as String] = true

数据保护钥匙串的授权模型完全不同:它不看二进制的代码签名 ACL,而是看 App 的 keychain-access-groups 权限——也就是按「这个 App ID / 团队有没有权访问这个访问组」来判定。而 App ID 和团队是稳定的,不随你重新编译变化。于是 cdhash 再怎么变,只要还是同一个 App ID、同一个团队签的,就一路放行,弹窗消失

代价是它有前提:App 得携带 keychain-access-groups 权限,并且用稳定的团队签名。所以这条路是「用一点配置换掉一个交互噪声」——entitlement 里声明访问组、项目里配好开发团队,弹窗就没了。

换钥匙串带来的迁移坑

这里有个必须交代的坑:传统钥匙串和数据保护钥匙串是两个独立的存储

切过去之后,数据保护钥匙串是空的——你之前写进传统钥匙串的那些密码、导入的私钥,在新的钥匙串里查不到。所以升级到这个版本的用户,已保存的主机密码和私钥得重新录入一次。这不是 bug,是两套存储物理隔离的必然结果,只能在升级说明里讲清楚。

顺带一个命名上的历史包袱:Shellby 这个产品原来叫 mShell,改名之后,钥匙串的 service 名依然沿用旧名 com.mshell.secrets——有意保留。因为一旦改了 service 名,等于又换了一次存储位置,用户已有的凭据全部读不到,白白再逼一次重录。改名是产品的事,存储键是数据的事,两者不能一起动。

顺带解决了同步

选数据保护钥匙串还有个附带收益,正好接上多端同步。数据保护钥匙串原生支持 iCloud 钥匙串的端到端同步——给钥匙串项加一个 kSecAttrSynchronizable = true,它就能跨设备同步,且 Apple 也读不了(端到端加密)。

本地密钥和同步密钥的可访问性还不一样:本地密钥用 …AfterFirstUnlockThisDeviceOnly(严格锁在这台设备),同步密钥必须放宽到 …AfterFirstUnlock(因为 synchronizable 不允许 ThisDeviceOnly)。查找和删除统一用 kSecAttrSynchronizableAny,这样开关同步、迁移前后都能命中。传统的文件型钥匙串在这些能力上远不如数据保护钥匙串顺手——换过去之后,「消除 macOS 弹窗」和「支持端到端同步」是一次选型解决的两件事。

小结

这个坑本身不大,但它是个很典型的例子:

  • 同一个「存密码」的需求,在 macOS 上有两套实现,授权模型完全不同。默认那套(传统 ACL)在开发期会被 cdhash 变化反复触发弹窗;
  • 换成数据保护钥匙串(按 App ID / 访问组授权,而非按代码签名 ACL)根治了弹窗,代价是要声明 entitlement、稳定团队签名;
  • 换存储要认迁移成本:两套钥匙串物理隔离,切过去老数据读不到,得让用户重录,并且别顺手改 service 名再逼一次;
  • 附带好处:数据保护钥匙串原生支持 iCloud 端到端同步,一次选型顺带解决了跨设备凭证同步。

有些「用户体验问题」的根子不在你的代码逻辑里,而在你选了平台的哪一套底层机制。弹窗不是你写错了,是你选错了钥匙串。

留言

  • 加载中…

留言先审后发,通过后公开显示;邮箱只有站主可见。