☰
用 Secure Enclave 保护 SSH 密钥:Secretive 的存储、认证与使用全解析
2026/9/25 3:41:06 网站建设 项目流程
  • 桌面应用
  • 应用安全
  • 密码学

【免费下载链接】secretive

Protect your SSH keys with your Mac's Secure Enclave

项目地址:https://gitcode.com/gh_mirrors/se/secretive
点击查看免费下载

Secretive 是一款面向 macOS 的 SSH 密钥管理与签名工具,核心思路是把私钥交给 Mac 的 Secure Enclave(安全隔区)硬件保管,私钥永不落盘、永不导出;同时通过 Touch ID、Apple Watch 等强访问控制来约束密钥的使用,并在每次签名时向你发出通知。本文基于仓库 README.md 展开,结合 SecureEnclaveStore.swift 等源码,讲解它的安全模型、安装方法、密钥创建选项、SSH agent 工作链路、智能卡支持以及常见排障与安全要点,读完后你可以完整评估并在自己的 Mac 上落地这套方案。

Secretive 是什么:把 SSH 私钥交给硬件隔离层

传统上,SSH 私钥以文件形式保存在磁盘上(如~/.ssh/id_ed25519),靠文件权限保护。只要攻击者拿到文件读取权限,私钥就可能被复制和滥用。Secretive 改变了这个模型:密钥由 macOS 的 Secure Enclave 协处理器生成和保管,操作系统与用户拿到的只是它的"不透明表示",真正的密钥材料不可导出。

用项目文档的原话来说:"It's impossible to export them, by design"——由于 Secure Enclave 的硬件约束,私钥在物理上就无法被导出,这从架构层面消灭了"私钥文件被拷贝"这一类风险。在 SECURITY.md 中,作者进一步给出了三条设计原则:

  1. 难以泄露(Hard to Leak):Secretive 只操作硬件托管的密钥,应用自身也读不到私钥明文,即使存在 bug 也很难导致密钥被分享。
  2. 简单与可审计(Simplicity and Auditability):刻意不无限堆砌功能,保证代码库规模可控、用户可以合理审计。
  3. 零第三方依赖(Dependencies):除构建过程外,App 本身不依赖任何第三方代码,从源头规避供应链攻击。

Secretive 由两个主要部分组成:一个是用户可见的管理界面 App,另一个是后台的 SecretAgent 进程。Agent 以 SSH agent 的形式监听 Unix socket,git/ssh等客户端通过SSH_AUTH_SOCK环境变量找到它并请求签名,签名请求再被转交给对应密钥所在的 Store(Secure Enclave 或智能卡)执行。

为什么选择它:四大核心能力

更安全的存储(Safer Storage)

磁盘上的私钥文件即便权限设置正确,也挡不住恶意软件直接复制。Secure Enclave 的密钥由于不可导出,天然具备"无法被偷走"的属性。在源码层面,SecureEnclaveStore.swift 的saveKey注释明确说明:存进 Keychain 的Data"isnotactual key material. This is an opaque data representation that the SEP can manipulate"——即只是安全隔区可操作的不透明表示,而非可复用的私钥数据。

强访问控制(Access Control)

Secure Enclave 支持 macOS 的强认证机制:Touch ID、Apple Watch,或密码。你可以在创建密钥时指定"使用前必须通过 Touch ID(或 Apple Watch)认证",这样即使恶意进程拿到了签名权限,也无法在你不授权的情况下使用密钥。

每次使用都有通知(Notifications)

密钥被访问时 Secretive 会弹出系统通知,你永远不会对密钥的使用"毫不知情"。通知机制由SigningWitness协议驱动——在 SigningWitness.swift 中,它定义了签名前的拦截钩子speakNowOrForeverHoldYourPeace和签名后的记录钩子witness,Agent 在真正执行签名前后都会调用它。

智能卡同样支持(Support for Smart Cards Too)

没有 Secure Enclave 的旧款 Mac,可以改用智能卡(如 YubiKey)来完成同样的硬件签名。这一能力由 SmartCardStore.swift 实现,它通过TKTokenWatcher监听智能卡插入/拔出,并用 Keychain 的kSecAttrTokenID查询卡上的私钥执行签名。

工作原理:从 SSH 客户端到 Secure Enclave 的完整链路

1. SSH agent 与 socket

Secretive 的 SecretAgent 进程在Sources/SecretAgent/AppDelegate.swift中装配:SocketController在指定路径创建 Unix socket(正式构建为socket.ssh,调试构建为socket-debug.ssh,见 URLs.swift),客户端通过SSH_AUTH_SOCK指向该路径。SocketController负责接受连接,并为每个连接建立独立Session,同时用SigningRequestTracer追踪是哪个进程发起的请求(来源溯源)。

2. Agent 解析请求并匹配密钥

Agent.swift 是 SSH agent 协议的实现核心,它处理两类主要请求:

  • requestIdentities:枚举当前可用的身份(公钥),返回给客户端;枚举时还会附带证书(certificates)身份;
  • signRequest:解析待签名数据、识别目标(SSH 连接或 SSH 签名),按 key blob 匹配到对应密钥,然后调用sign。

sign方法(同文件 L148-L166)的执行顺序是:先通过witness?.speakNowOrForeverHoldYourPeace通知"即将使用密钥",再调用store.sign(...)真正签名,最后通过witness?.witness记录一次访问。这正好对应了"使用前可拦截、使用后留通知"的体验。

3. 签名落在硬件上

Secure Enclave 的签名实现在 SecureEnclaveStore.swift 的sign方法中:先用SecItemCopyMatching从 Keychain 取出密钥的不透明表示,再交给 CryptoKit 的SecureEnclave.P256.Signing.PrivateKey(或 macOS 26 上的MLDSA65/87)在安全隔区内完成签名,密钥明文始终不进入应用内存。如果密钥配置了认证要求,这里会创建LAContext触发 Touch ID / Apple Watch 弹窗;如果此前已通过"临时授权"(persistAuthentication)机制持久化了认证,则会复用已存在的认证上下文,避免短时间内反复弹窗。

4. 密钥的存储抽象

源码中SecretStore协议(SecretStore.swift)统一了不同后端的操作接口:sign(签名)、persistAuthentication(持久化授权)、reloadSecrets(重新加载),可修改型SecretStoreModifiable还支持create/delete/update。Secure Enclave 与智能卡分别是它的两个实现,界面层面对用户暴露统一的"密钥"概念,这就是为什么你能在同一界面管理两种来源的密钥。

安装与快速开始

方式一:直接下载

从项目的 Releases 页面下载最新的.zip包解压后,把Secretive.app拖入"应用程序"文件夹即可。由于构建是可审计的(见下文"安全模型"一节),你还可以把下载包的 SHA 与构建日志中记录的 SHA 核对后再运行。

方式二:Homebrew

brew install secretive

让 SSH 客户端认识 Agent

Secretive 依赖SSH_AUTH_SOCK环境变量被客户端正确尊重。git和ssh命令行工具原生支持该变量,安装后它们会自动通过 Agent socket 完成签名;但部分第三方 Git GUI 客户端需要手动配置环境变量才能工作。若遇到"Secretive 在我的 git 客户端里不生效"的情况,先确认客户端是否透传了SSH_AUTH_SOCK,仓库 FAQ 中也给出了针对多种客户端的配置说明(见 FAQ.md)。

代码签名与 Keychain 的注意点

README 特别提醒:虽然密钥由 Secure Enclave 保护,但密钥的存取仍依赖 Keychain API。Keychain 会把密钥的读取权限限制在创建它的 App(具体到 bundle ID)。因此如果你从源码自行构建 Secretive,必须保持 bundle ID 前后一致(仓库根目录提供了 configure_team_id.sh 帮助配置),否则 Keychain 将无法定位到你之前创建的密钥;同理,用自编译版本也无法读取预编译版本创建的密钥。

密钥创建与访问控制选项

在 Secretive 中新建密钥时,可以指定两项核心配置,对应源码中的Attributes结构(CreationOptions.swift):

配置项可选值说明
算法/密钥类型ecdsa256、ecdsa384、mldsa65、mldsa87、rsa2048由KeyType定义(Secret.swift)
认证要求notRequired/presenceRequired/biometryCurrent决定使用密钥前是否需要认证

算法支持的真实边界

需要特别澄清的是:Mac 的 Secure Enclave 只支持 256 位 EC 密钥,因此 Secretive 无法生成 RSA 密钥(FAQ 明确说明)。从 SecureEnclaveStore.swift 的实现看,Secure Enclave Store 实际可用的是ecdsa256,另在 macOS 26 及以上额外支持mldsa65/mldsa87(不满足系统版本时,界面会把它们列为不可用并标注macOSUpdateRequired原因)。ecdsa384与rsa2048等类型仅存在于KeyType定义层,Secure Enclave 侧并不支持。如果你的场景必须使用 RSA,需要通过智能卡(如 YubiKey)或其他工具满足。

三种认证要求的行为差异

  • notRequired(无需认证):使用密钥不弹窗。创建时对应的SecAccessControlCreateFlags只包含.privateKeyUsage。
  • presenceRequired(用户在场):使用前需要通过生物识别、已配对的 Apple Watch 或密码认证。对应.userPresence标志,是"用前必须验身"的默认推荐档。
  • biometryCurrent(仅当前生物特征):只接受创建时刻录入的那组生物特征(例如某个特定指纹),新增任何指纹都会导致该密钥彻底无法访问,且无法用密码绕过。源码注释直接将其标注为"a dangerous option prone to data loss"——配置此选项前,必须向用户充分警告。

另外注意:Attributes.authentication只是创建时记录的一份描述,修改它并不会真正改变密钥的认证行为——真正的约束由创建时写入 Keychain 的SecAccessControl决定。

指定公钥路径

从 Secretive 2.2 起,每个密钥都会在磁盘上自动生成一份公钥文件表示,其路径显示在 App 的 "Public Key Path" 字段。生成逻辑见 URLs.swift:公钥文件按 OpenSSH MD5 指纹(去掉冒号)命名,存放在PublicKeys目录下。你可以把该路径写进~/.ssh/config,指定某个主机使用这把密钥:

Host myserver IdentityFile /path/to/xxx.pub

智能卡(YubiKey 等)的使用

对于没有 Secure Enclave 的 Mac,可以插入智能卡并直接用于签名。实现上,SmartCardStore.swift 通过TKTokenWatcher监听令牌插入事件(并主动排除名为setoken的 Secure Enclave 令牌),插入后以kSecAttrTokenID为条件从 Keychain 查询卡上的私钥,用SecKeyCreateSignature执行签名。由于智能卡私钥由卡片本身保护,创建/导出策略取决于卡厂商软件——部分卡片是可能通过厂商工具导出私钥的,这与 Secure Enclave 的"绝对不可导出"不同。

常见使用场景与 FAQ 要点

密钥能导入导出吗?

Secure Enclave 密钥不行:不能导入旧密钥,也不能导出。换新 Mac 时直接为新机器创建一套新密钥即可(README "Backups and Transfers to New Machines" 一节明确说明)。智能卡密钥则视厂商软件而定,有可能导出。

支持 SSH Agent Forwarding 吗?

支持。在~/.ssh/config中为相关主机加上:

Host remotehost ForwardAgent yes

之后在远程主机上使用你的任一密钥,都必须经过本机 Secretive 的认证,远程主机无法独立使用你的密钥。

设置了 Apple Watch 却仍弹密码?

按 FAQ 的排查步骤:先在"系统设置 → 触控 ID 与密码"(或旧版"系统偏好设置 → 安全性与隐私")中开启"使用 Apple Watch 解锁 App 和 Mac";然后至少完成一次"锁定并解锁 Mac"让手表同步生效;之后密钥被访问时会在手表上弹出确认,双击侧边按钮即可批准。

为什么有时弹出密码而非生物识别?

通常是认证上下文被降级(例如 Touch ID 连续失败或系统策略限制),此时 macOS 会退回密码输入。若该密钥创建时选择的是biometryCurrent,则密码无法作为替代手段。

我可以生成 RSA 密钥吗?

不能,理由同上:Secure Enclave 仅支持 256 位 EC 密钥。若必须用 RSA,请使用智能卡方案。

安全模型与可审计构建

安全策略

详细策略见 SECURITY.md,核心包括前文提到的"硬件密钥不可读、代码简单可审计、零第三方依赖"三大原则。受支持版本为 Releases 页面的最新版本;发现漏洞请通过 GitHub 的私有报告(Private Reporting)渠道提交。

可审计的构建过程

从 Secretive 3.0 开始,所有发布版均由 GitHub Actions 构建,并使用 GitHub Artifact Attestation 对构建产物做签名背书。每条构建都有完整可审计的构建日志,记录源码来源与产物的 SHA 值;你可以在"关于(About)"窗口找到构建日志链接,把自己下载的 zip 的 SHA 与日志中的 SHA 比对,确认你运行的就是日志中记录的产物。这是"零第三方依赖"原则在分发环节的延伸。

那一次对 GitHub 的网络请求是什么?

Secretive 会在启动时检查更新:Updater(Updater.swift)默认以 24 小时为周期(含 1 小时容差)调用 GitHub 的 releases API,用语义化版本比较选出"高于当前版本、非预发布、且最低系统版本 ≤ 当前系统"的最新版本并提示。你可以在"关于"窗口中点击"检查更新"手动触发;对某个版本点"忽略"后,它的名字会被记入com.maxgoedjen.Secretive.updater.ignorelist这个 UserDefaults,但关键安全更新(critical)无法被忽略。

故障排查与卸载

通用排障流程

  1. 在终端执行ssh -Tv git@github.com,检查是否通过SSH_AUTH_SOCK连上了 Secretive 的 Agent。
  2. 若之前正常、现在突然失效,先尝试菜单栏"帮助(Help)→ Setup Secretive"重新走一遍设置流程。
  3. 若仍有问题,把ssh -Tv的输出连同问题描述一起反馈给维护者。

卸载方式

将Secretive.app拖入废纸篓,并删除容器目录~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent。注意 SecretAgent 进程可能在你退出 App 或重启前仍在后台运行,需要手动结束或重启系统。

小结

Secretive 把"SSH 私钥保护"这件事从文件权限提升到了硬件隔离层面:Secure Enclave 保证密钥不可导出,访问控制保证密钥不被偷用,通知机制保证每次使用都可感知,智能卡支持则补全了旧设备的可选路径。对于希望深度审计或自行构建的用户,整个项目(管理界面、Agent、协议层、两种 Store 实现)都在本仓库内可直接查阅,且除构建过程外不依赖任何第三方代码。若你重视 SSH 密钥的物理级安全与使用透明度,Secretive 是一套架构清晰、可验证的实践方案。

  • 桌面应用
  • 应用安全
  • 密码学

【免费下载链接】secretive

Protect your SSH keys with your Mac's Secure Enclave

项目地址:https://gitcode.com/gh_mirrors/se/secretive
点击查看免费下载
上一篇:GitHubDaily开源奖项:申请与获得行业认可的技巧
下一篇:终极SublimeLinter完全指南:如何轻松实现代码质量革命

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询