- 桌面应用
- 应用安全
- 密码学
【免费下载链接】secretive
Protect your SSH keys with your Mac's Secure Enclave
导读
Secretive 是一个使用 Mac 的 Secure Enclave(安全隔区)来保护和管理 SSH 密钥的开源应用。本文以仓库根目录下的 SECURITY.md 安全策略文档为骨架,逐条拆解其三大安全设计原则——"硬件背书密钥""简洁可审计""零第三方依赖",并深入对应源码(SecureEnclaveStore.swift、SecureEnclaveSecret.swift、CreationOptions.swift 等)验证其实现细节。读完本文,你将理解:为什么"私钥永远无法被导出"是 Secretive 安全模型的根基、密钥创建时访问控制选项(Touch ID / 密码 / 生物特征锁定)在系统层如何落地、零依赖策略如何在 Package.swift 中体现,以及如何按官方流程向项目方报告安全问题。
一、安全策略总览:三条设计原则
SECURITY.md 开篇即阐明 Secretive 安全模型的整体思路,不依赖单一防御点,而是由三条相互支撑的原则构成:
| 原则 | 核心思想 | 威胁面覆盖 |
|---|---|---|
| 硬件背书密钥(Hardware-Backed Keys) | 私钥材料只存在于 Secure Enclave 内部,应用本身永远无法读取 | 应用自身 Bug、恶意代码复制私钥 |
| 简洁性与可审计性(Simplicity and Auditability) | 有意不追求全功能,保持代码库规模可控,让社区可以真正完成审计 | 代码复杂度带来的隐藏缺陷 |
| 零第三方依赖(No Dependencies) | 应用运行时不依赖任何第三方代码 | 供应链攻击、上游投毒 |
以下各节逐一深入:先讲设计意图,再用仓库源码印证其落地方式。
二、原则一:硬件背书密钥——"Secretive 读不到的密钥,就很难被泄露"
2.1 设计前提:私钥材料永不离开 Secure Enclave
安全策略原文用了一个很直白的表述:"It's Hard to Leak a Key Secretive Can't Read The Key Material"(Secretive 读不到密钥材料,所以很难泄露密钥)。其推理链条是:
- 私钥只以硬件密钥形式存在,由 Secure Enclave 安全处理器持有;
- 应用(包括 Secretive 自身)只能请求 Secure Enclave执行签名操作,而拿不到私钥本身;
- 因此,即使 Secretive 存在任何逻辑 Bug,攻击者也无法通过该 Bug 把私钥导出去——攻击面在物理上被封闭了。
这与传统"私钥以文件形式存放在磁盘上、靠文件权限守护"的方案形成本质对比:磁盘上的密钥只要权限被突破(恶意进程、恶意用户、备份泄露)即可被复制;而 Secure Enclave 密钥的不可导出是硬件设计使然。
2.2 源码证据:Secret 对象里根本没有私钥字段
从源码结构看,这一原则被严格贯彻到了数据模型中。SecureEnclave.Secret是 Secure Enclave 密钥在应用内的表示,完整定义见 SecureEnclaveSecret.swift:
public struct Secret: SecretKit.Secret { public let id: String public let name: String public let publicKey: Data public let attributes: Attributes }它只包含:唯一标识id、用户可读名称name、公钥publicKey、以及创建时的属性描述attributes。注意其中没有任何私钥字段。底层的 Secret 协议 同样只要求暴露name、publicKey、attributes三项。
也就是说,私钥数据从未进入应用的正常内存模型。应用持有的只是公钥和元数据,签名时再通过系统 API 把待签名数据交给 Secure Enclave 处理。
2.3 密钥创建:访问控制标记如何在系统层落地
密钥创建路径位于 SecureEnclaveStore.swift 的create(name:attributes:)。它调用SecAccessControlCreateWithFlags生成访问控制对象,并依据创建选项attributes.authentication映射为不同的系统级标记:
let flags: SecAccessControlCreateFlags = switch attributes.authentication { case .notRequired: [.privateKeyUsage] case .presenceRequired: [.userPresence, .privateKeyUsage] case .biometryCurrent: [.biometryCurrentSet, .privateKeyUsage] case .unknown: fatalError() } let access = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, flags, &accessError)其中.privateKeyUsage是所有选项的公共标记(表示该钥匙可被用于私钥操作),而认证要求的差异体现在:
| 创建选项(AuthenticationRequirement) | 追加的系统标记 | 用户侧行为 | 备注 |
|---|---|---|---|
notRequired | 仅.privateKeyUsage | 使用密钥时无需任何认证 | 最方便,但任何调用方进程都可触发签名 |
presenceRequired | .userPresence+.privateKeyUsage | 每次使用需通过 Touch ID、已配对的 Apple Watch 或密码认证 | 对应 CreationOptions.swift 中的描述 |
biometryCurrent | .biometryCurrentSet+.privateKeyUsage | 仅接受创建时录入的那一组生物特征 | 危险选项,详见下文警告 |
AuthenticationRequirement枚举的完整定义在 CreationOptions.swift。其中对biometryCurrent有明确的高风险警告:一旦用户后续修改生物特征注册(哪怕只是新增一个指纹),该密钥将永久无法再被访问,且不能用密码覆盖。源码注释将其标注为 "a dangerous option prone to data loss"(容易导致数据丢失的危险选项)。
此外,kSecAttrAccessibleWhenUnlockedThisDeviceOnly意味着密钥在设备解锁期间可用,且绝不随备份同步迁移到其他设备——这与后文"密钥不可备份、不可迁移"的约束完全一致。
2.4 钥匙串里存的到底是什么:不是密钥材料本身
密钥最终通过saveKey(_:name:attributes:)写入钥匙串(Keychain),见 SecureEnclaveStore.swift。这段代码上的注释是理解整个安全模型的关键:
"Despite the name, the 'Data' of the key isnotactual key material. This is an opaque data representation that the SEP can manipulate."
即:写入钥匙串的kSecValueData不是真正的密钥材料,而是一种 Secure Enclave 处理器(SEP)才能操纵的不透明数据表示。应用把它保存下来只是为了让 SEP 后续能定位并"复活"对应的密钥,拿到这份数据的人也无法还原出私钥。
实际存储时以通用密码条目(kSecClassGenericPassword)落库,服务标识为com.maxgoedjen.secretive.secureenclave.key(见 SecureEnclaveStore.swift),并用kSecAttrAccount记录密钥 UUID、kSecAttrGeneric存放编码后的创建属性(AttributesJSON)、kSecAttrLabel存放用户命名。
2.5 签名流程:私钥从未离开安全区
当 SSH 客户端请求签名时,流程位于 SecureEnclaveStore.swift 的sign(data:with:for:target:):
- 从钥匙串取回密钥的不透明数据表示与属性;
- 根据
authentication需求构造LAContext并设置localizedReason(向用户展示的认证原因文案,会包含请求方应用名、目标主机、密钥名等上下文); - 调用
CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation:authenticationContext:)重建密钥句柄; - 由 Secure Enclave 完成签名,应用只拿到签名结果
rawRepresentation。
关键点:应用传入的是待签名数据,取回的是签名结果,私钥始终只存在于安全区内。这正是第 2.1 节推理链的运行时体现。
从代码结构看,支持的密钥类型由supportedKeyTypes属性定义(SecureEnclaveStore.swift):ecdsa256在所有受支持系统上可用;mldsa65与mldsa87(ML-DSA 后量子签名算法)需要 macOS 26 及以上,旧系统上会以macOSUpdateRequired标记为不可用。包清单 Package.swift 声明的最低系统版本为 macOS 14。
2.6 没有 Secure Enclave 的 Mac:智能卡兜底
安全策略强调"Secretive 只操作硬件背书密钥"。"硬件背书"并不局限于 Secure Enclave——对于没有安全区的 Mac,SmartCardStore.swift 提供了基于智能卡(如 YubiKey)的等价实现:通过 CryptoTokenKit 的TKTokenWatcher监听卡片插入/拔出,签名时用SecKeyCreateSignature将数据交给卡片完成。两种后端共享同一个 SecretStore 协议,因此"应用拿不到私钥"的安全性质是一致的。
三、原则二:简洁性与可审计性
3.1 有意识地限制功能面
SECURITY.md 明确写道:"Secretive 不会扩展出它可能拥有的每一个功能。"其逻辑是:可审计性以代码规模为代价——如果一个功能虽然"很酷",但会显著膨胀代码库、让安全审计变得不现实,就宁可不做。
从仓库目录结构看,这一原则被认真执行:核心逻辑被拆分为多个职责单一的小包(SecretKit、SecureEnclaveSecretKit、SmartCardSecretKit、SSHProtocolKit、CertificateKit、Formatters),每个包都只解决一个明确问题,测试与源码一一对应(见 Sources/Packages 下的Sources/与Tests/布局)。
3.2 可审计的构建与发布流程
README.md 补充了构建环节的可审计性:自 Secretive 3.0 起,构建产物通过 GitHub Actions 生成,并使用 GitHub Artifact Attestation(构件证明)对发布进行签名背书,证明文件可在对应构建日志与 Attestation 页面核验。这让用户可以确认拿到的应用确实来自官方构建流程、未被篡改,是"可审计性"原则在供应链末端的延伸。
3.3 可审计性的直接佐证:签名请求溯源机制
虽然 SECURITY.md 未展开,但"让消费者能够合理审计"的精神贯穿到了签名请求处理链路。Agent.swift 在每次签名前会构造一个SigningRequestProvenance(请求溯源对象),它记录触发签名的完整进程链(例如git fetch会形成ssh→git→zsh→login→Terminal.app的链条),并逐一校验链上每个进程的代码签名是否有效(intact属性),见 SigningRequestProvenance.swift。这保证了用户界面展示的"谁在请求使用你的密钥"是经过系统级校验的、可信任的信息——审计不只针对代码,也针对每一次密钥访问。
四、原则三:零第三方依赖
4.1 双重动机:可审计性 + 供应链安全
SECURITY.md 指出零依赖策略同时服务两个目标:其一,进一步支撑"可审计性"——不用逐行审查第三方库,审计工作量被严格限定在本仓库代码内;其二,彻底排除供应链攻击——第三方依赖一旦被投毒(dependency confusion、上游仓库被入侵、恶意版本发布等),影响面将不可控。
4.2 源码证据:Package.swift 的依赖数组为空
仓库根目录的 Package.swift 中:
dependencies: [ ],SwiftPM 清单的依赖列表为空;Sources/Packages/Package.swift 也保持同样的结构。所有功能均基于 Apple 系统框架(Security、CryptoKit、LocalAuthentication、CryptoTokenKit、OSLog等)与仓库自研包实现。例如 SSH 协议编解码、OpenSSH 公钥/签名/证书的读写,全部由 SSHProtocolKit 内自研实现,并有对应测试(SSHProtocolKitTests)覆盖。
4.3 例外说明:仅限构建过程
SECURITY.md 也给出了诚实的例外声明:构建流程中存在少量第三方工具(例如测试与发布管线使用的 CI 基础设施),但应用本身不依赖任何第三方运行时代码。这意味着你审计的目标是清晰的——运行时攻击面就是本仓库 + Apple 系统框架。
五、支持的版本
SECURITY.md 对版本支持策略的定义非常简洁:只有 GitHub Releases 页面上的最新版本是当前受支持的版本。
实际含义有两点:
- 旧版本不会获得安全补丁;发现漏洞后,修复只以新版本形式发布;
- 使用者应保持更新到最新版,并尽量通过官方渠道获取(README.md 提供的两种方式:直接下载最新 Release,或
brew install secretive通过 Homebrew 安装)。
由于构建产物附带 Artifact Attestation(见 3.2 节),建议在安装时核对构建证明,确保获取的是官方构建。
六、如何报告漏洞
6.1 官方指定渠道:GitHub Private Reporting
SECURITY.md 指定的漏洞报告方式是GitHub 的私有报告功能(Private Vulnerability Reporting)。该功能保证:
- 报告内容在修复前对公众不可见,避免漏洞细节过早泄露导致被利用;
- 维护者可以在私有环境里与报告者协作、讨论修复方案;
- 只有确认修复并发布后,才会进入公开的 Security Advisory 流程。
实际操作路径为:进入仓库主页 →Security标签页 →Report a vulnerability(报告漏洞)→ 按表单填写详情并提交。这是目前与"仅支持最新版本"策略配套的标准处置闭环:报告 → 私有讨论 → 修复 → 发布新版本 → 公开披露。
6.2 高质量报告的建议要素
虽然 SECURITY.md 未逐条列出,但结合本仓库特点,一份有效的报告通常应包含:
- 受影响版本与系统环境:macOS 版本、Secretive 版本号、是 Release 构建还是自源码构建(注意 README.md 关于 bundle ID 一致性的说明,见下节);
- 可复现步骤:最小化的触发流程;
- 影响评估:该问题是否可能破坏"私钥不可导出""访问需认证"等核心安全性质;
- 建议修复方向:可选的补丁思路,供维护者快速评估。
6.3 为什么不要公开披露
对于密钥管理类项目,漏洞信息在修复前的公开披露会直接放大风险:攻击者可能抢在修复前利用细节发起攻击。私有报告机制正是为了压缩这个"暴露窗口"。
七、README 中的补充安全实践(与 SECURITY.md 配套阅读)
README.md 在安全话题上还有三条与 SECURITY.md 强相关的实践提醒,一并纳入本文:
7.1 代码签名与钥匙串的绑定:保持 bundle ID 一致
README 明确指出:Secretive 虽然用 Secure Enclave 保护密钥,但仍然通过 Keychain API 来存储与访问密钥;而 Keychain 会将密钥的读取权限限制到创建该密钥的那个应用(具体到 bundle ID)。因此,如果你从源码自行构建,必须始终使用同一个 bundle ID,否则 Keychain 将无法定位你的密钥。这其实是第 2.4 节存储机制的用户侧推论:密钥条目的归属与签名应用强绑定。
7.2 密钥不可备份、不可迁移
因为 Secure Enclave 密钥不可导出,它们无法备份,也无法迁移到新机器。换机时正确的做法是:在新 Mac 上创建一套新密钥,旧机器上的密钥随设备共存亡。这是"不可导出"安全性质必须接受的运维代价,也是使用本类方案前需要明确的心理预期。
7.3 访问通知:让每次密钥使用透明
README 与 SECURITY.md 之外,应用会在密钥被访问时弹出通知(Notifier,见 Notifier.swift),结合 3.3 节的请求溯源机制,向用户展示"哪个应用、哪条进程链在请求使用你的密钥"。这保证了即使恶意软件尝试触发签名,用户也能在第一时间察觉异常——硬件背书解决了"导不走",通知机制则解决了"被滥用时看不见"。
八、总结
SECURITY.md 用极简篇幅勾勒了 Secretive 的完整安全立场,而源码将每条原则落到了实处:
- "读不到就难泄露":私钥只以硬件密钥形式存在于 Secure Enclave / 智能卡中,应用内存模型里只有公钥与不透明数据表示,签名由硬件完成;
- "简洁可审计":刻意控制功能面,构建过程可验证(Artifact Attestation),每次签名请求都带可校验的进程溯源;
- "零依赖":运行时零第三方代码,供应链攻击面趋近于零,审计边界清晰。
对于使用者,这份策略的实际含义是:私钥导出在物理上不可能、旧版本不受支持需保持更新、漏洞应通过私有报告渠道提交、密钥无法备份迁移需提前规划换机流程。这些约束共同构成了一个以"硬件隔离 + 小代码面 + 供应链洁癖"为核心的可审计安全模型。
如果你希望进一步验证本文引用的实现细节,可以直接查阅仓库中的 SECURITY.md、README.md、SecureEnclaveStore.swift、CreationOptions.swift 以及 Package.swift。
- 桌面应用
- 应用安全
- 密码学
【免费下载链接】secretive
Protect your SSH keys with your Mac's Secure Enclave
相关推荐
Secure Enclave终极安全指南:如何用Secretive保护你的SSH密钥
Secure Enclave终极安全指南:如何用Secretive保护你的SSH密钥 Secretive是一款专为macOS设计的安全工具,通过利用Mac的Se
桌面应用应用安全密码学Secretive 常见问题深度解析:Secure Enclave 保护 SSH 密钥的实用排障指南
Secretive 常见问题深度解析:Secure Enclave 保护 SSH 密钥的实用排障指南 Secretive 是一款用 Mac 的 Secure E
桌面应用应用安全密码学AVR-HAL外设驱动实战:I2C、SPI与UART通信接口开发指南
AVR HAL外设驱动实战:I2C、SPI与UART通信接口开发指南 AVR HAL是为AVR微控制器提供embedded hal抽象的强大框架,让开发者能够轻
嵌入式
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考