Windows 本地文件保险箱的一个实现思路:VHDX + NTFS + BitLocker
电脑真正让人不放心的时候,往往不是丢了,而是还在自己手边。
同事临时借一下电脑,维修店让你把机器留下,客户坐在旁边等你打开资料,家里电脑要给别人用一会儿。你知道哪些东西不该被看到,但它们散在桌面、下载、相册备份、聊天文件夹和几个项目目录里。临时移动一遍不现实,删掉更不现实。
如果从 Windows 桌面程序的角度看,这个问题并不只是“把文件加密”这么简单。用户要的不是一串加密后的字节,而是一个平时能正常拖拽、打开、保存,离开时能锁住,换机器或出问题时还有恢复路径的文件空间。
所以我更倾向于把本地文件保险箱拆成三层:VHDX 做容器,NTFS 继续承担普通文件系统语义,BitLocker 负责卷级加密。应用程序不要去重新发明加密格式,只要把创建、挂载、解锁、锁定、卸载这些生命周期管清楚。
问题不是加密算法,而是文件怎么被正常使用
自己做一个加密文件格式,看起来很诱人。读文件、加密、写回,逻辑很直观。真放到桌面工具里,很快就会遇到几个小麻烦。
Office 会生成临时文件,图片编辑器可能直接覆盖保存,很多软件会先写一个中间文件再重命名。用户也不会只通过你的程序操作文件,他会从资源管理器拖进去,从另一个程序里“另存为”,或者直接在目录里改名。
如果应用自己维护一套“虚拟文件列表”,就得把这些 Windows 文件操作习惯重新实现一遍。做得轻了,体验不像一个正常磁盘;做得重了,基本是在补一个小型文件系统。隐私工具本来要解决的是电脑借人、送修、临时离开、私人照片和证件长期保存这类问题,不应该把主要复杂度花在重做文件系统上。
VHDX、NTFS 和 BitLocker 各自负责什么
一个比较稳的拆法是这样:
私密文件 -> 放入 VHDX 容器 -> NTFS 提供普通磁盘体验 -> BitLocker 负责锁定状态下的卷级加密 -> 应用程序管理挂载、解锁、锁定、卸载VHDX 解决的是“把一组文件装进一个容器”。它本质上仍然是 Windows 熟悉的虚拟磁盘格式,不需要应用自己定义一套文件布局。
NTFS 解决的是“容器里怎么像普通磁盘一样使用”。复制、移动、保存、重命名、临时文件、权限和崩溃后的文件系统一致性,都交给成熟文件系统处理。
BitLocker 解决的是“这个卷在锁定状态下如何加密保护”。它不是应用里的一个按钮,而是卷生命周期的一部分:启用、解锁、锁定、恢复,这些状态要被程序清楚地区分。
这样做不代表万事大吉。它只是把问题放回 Windows 擅长的位置上。应用层仍然要处理目标归属、权限、错误提示、异常中断和用户恢复路径。
桌面程序不要直接把高权限动作塞进 UI
文件保险箱一旦涉及挂载虚拟磁盘、启用或解锁 BitLocker、锁定卷、卸载卷,就很容易碰到权限边界。最省事的做法是让整个桌面界面以管理员身份运行,但这也是风险最不好控制的做法。
界面进程里有大量用户输入、网页控件、文件选择器和插件式组件。把它们全部放进高权限进程,只是把攻击面扩大了。更稳的结构是把交互和系统动作分开:
普通用户界面 -> 提交结构化请求 -> 本地 IPC -> 后台服务校验目标和权限 -> 执行挂载 / 解锁 / 锁定 / 卸载 -> 返回结构化结果这里的重点不是 IPC 选什么,而是边界要清楚。管道只是传输层,不是信任边界;“请求来自本机”也不等于“请求可以执行”。服务端应该重新检查命令类型、参数大小、目标路径、容器状态和当前操作是否允许。
return request.Kind switch { "QueryVaultStatus" => QueryVaultStatus(request), "OpenVault" => ValidateThenOpenVault(request), "LockVault" => ValidateThenLockVault(request), _ => Result.Rejected("unsupported_command") };这段只是表达思路:高权限服务接收的是有限命令集,而不是一段命令行、脚本或者可以自由解释的字符串。
错误处理不能只返回一段字符串
本地保险箱的失败状态很多。容器文件不存在、卷已经挂载、密码错误、设备不在场、服务不可用、操作超时、用户取消、系统正在占用目标卷,都不能简单归成“打开失败”。
如果服务端只返回一段错误文本,UI 很快会被迫做字符串匹配。今天判断“busy”,明天换一个系统语言或者底层异常文本变了,逻辑就失效了。
public sealed record VaultResult( bool Success, string Code, string UserMessage, string? DebugDetail);Code 是程序之间的稳定契约,UserMessage 只解释用户能理解的结果和下一步,DebugDetail 留给日志或支持流程。尤其是涉及密码、恢复材料和用户文件路径时,更不能把调试信息直接弹给普通用户。
这种设计适合什么场景
它最适合的不是“每天研究加密”的人,而是普通 Windows 用户很具体的几个时刻:
电脑借给别人之前,想把照片、证件、合同和聊天备份先收起来;电脑送修前,不想临时翻遍每个目录;办公室电脑同时放着工作文件和私人文件,希望长期分开放;临时离开座位时,至少知道重要文件不是散落在桌面和下载目录里。
这些场景里,本地文件保险箱的价值不是把安全说得神乎其神,而是让文件有一个明确的位置,有一个明确的打开和收起动作。平时像普通磁盘一样用,需要保护时能锁住,出了问题还能按卷和容器的思路去排查。
我们在超级看门狗里的取舍
我们做超级看门狗 SuperWatchDog 时,也采用了类似取舍。用户看到的是 .dogvault 保险箱文件,底层用固定大小 VHDX + NTFS + BitLocker 承接长期私密文件。它不是重新发明一套加密算法,而是把 Windows 已有能力组织成普通用户能理解的保险箱流程。
另一个问题是“眼前来不及怎么办”。比如有人突然靠近电脑,或者临时要离开座位,用户不一定还有时间整理文件、切窗口、关程序。超级看门狗把手动、快捷键、USB 拔出、蓝牙断开等触发方式放在同一套保护思路里:长期文件放进保险箱,临时场景用预先设置好的保护动作处理。
这两件事最好分开看。BitLocker 负责的是锁定状态下的卷级加密;应用程序负责的是让用户知道文件放在哪里、什么时候打开、什么时候收起,以及失败时下一步该怎么做。边界越清楚,工具反而越容易给普通人使用。
