ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

用 Rust 造了一把密码锁:从零写一个本地离线密码管理工具的完整实践

2026/9/27 17:17:14 拓冰建站 浏览量
用 Rust 造了一把密码锁:从零写一个本地离线密码管理工具的完整实践 用 Rust 造了一把密码锁从零写一个本地离线密码管理工具的完整实践开发一个纯本地 · 零网络 · 开源的密码本是一种怎样的体验这篇文章记录了我用 Rust egui SQLite 构建密码本 v1.0.0 的完整过程包括加密选型、架构设计、踩坑记录和最终发布。一、为什么又造一个密码管理工具先说背景。我的桌面上躺着 7 个密码记录本Chrome 保存了 124 个账号Edge 保存了 89 个还有一堆写在便签上、Excel 里、甚至印象笔记里的散密码。每到又忘了这个网站密码的时候内心都有一种深深的无力感。Bitwarden、1Password、Dashlane 都很好但我总有些别扭它们要么是 SaaS数据在别人服务器上要么是开源但依然要账号登录我不想把我的所有密码托付给任何一家公司我是 Rust 爱好者就想写一个自己的所以我决定用 Rust 从零造一个密码本。目标很简单三条铁律✅纯本地— 所有数据只在我自己电脑上不上传任何服务器✅完全离线— 启动后零网络请求断网也能用✅开源可审计— 代码在 GitHub谁都能看二、技术选型为什么是这些需求选择理由GUIeframe / egui 0.30Rust 最成熟的即时模式 GUI跨平台、编译型、零运行时数据库SQLite (rusqlite)嵌入式、单文件、无服务器完美匹配本地存储需求加密AES-256-GCM PBKDF2NIST 推荐的对称加密工业级标准实现用 audited crate托盘tray-iconWindows 系统托盘支持线程模型踩了大坑后文详述剪贴板arboard跨平台剪贴板库Rust 生态首选资源注入winresource构建时把图标和版本信息嵌入 exe构建Cargo build.rs自动从 PNG 生成 ICO一步到位加密为什么选 AES-256-GCM主密码 ──PBKDF2-SHA256──▶ 32字节密钥 │ ▼ 每条记录独立 Nonce ──AES-256-GCM──▶ 密文 认证 TagAES-256-GCM不仅加密还带认证Authenticated Encryption。如果密文被篡改解密时会失败——这比单纯加密多了一层完整性保护。PBKDF2为什么不用更现代的 Argon2其实 Argon2 更好但很多密码管理器的思路是我不是在跟 GPU 农场对抗60 万次 PBKDF2 迭代已经足够让暴力破解变得非常昂贵单 GPU 每小时尝试次数屈指可数。而且 PBKDF2 是 NIST SP 800-132 标准合规性好。关键设计每条密码记录独立 Salt 和 Nonce。相同的主密码 相同的明文 → 加密后密文完全不同。防止彩虹表攻击防止两条记录密文一样这种信息泄露。三、踩坑记录重点 坑 1托盘图标创建在主线程 永远不响应这是整个项目最折磨我的 bug前后修了5 次。现象托盘图标显示了但是点了没反应菜单也不弹。根因tray-icon 在 Windows 上会创建一个隐藏窗口类名tray_icon_app这个窗口有自己的 WndProc需要跑PeekMessage/DispatchMessage消息泵才能接收系统托盘通知。在哪个线程创建这个隐藏窗口那个线程就必须有自己的消息泵。eframe 主线程的 winit 消息循环只关心自己的主窗口 HWNDdispatch 自己主窗口的消息队列。tray-icon 的隐藏窗口消息队列完全被忽略。修复// 托盘必须在独立线程创建 跑自己的消息泵 std::thread::spawn(move || { let tray TrayIconBuilder::new().build().unwrap(); loop { // 这个循环是本线程的消息泵 let mut msg zeroed::MSG(); PeekMessageW(mut msg, null_mut(), 0, 0, PM_REMOVE); TranslateMessage(msg); DispatchMessageW(msg); // 然后 try_recv tray-icon channel 处理事件 } });这个坑我花了整整一天最终在 tray-icon 的 GitHub Issues 里找到线索。 坑 2窗口 Visible(false) 后 eframe 停止调用 update()现象关闭窗口退托盘后托盘线程写了show_requestedtrue但主线程的update()再也没被调用过永远读不到那个标志。根因eframe 0.30 在主窗口被设为隐藏后有一个渲染节流逻辑——既然窗口都不显示了就没必要继续重绘了。结果update()不被调用 → 托盘事件不被处理。修复关闭窗口用Minimized(true)而不是Visible(false)// 关闭 → 最小化保持 update 循环运行 ctx.send_viewport_cmd(ViewportCommand::Minimized(true));这样窗口缩小到任务栏update() 循环继续跑托盘线程和主线程都活着。 坑 3winres 的 resource.lib 被链接器跳过现象build.rs 生成了 .ico 文件rc.exe 编译成功resource.lib 也有了但 exe 里完全没有.rsrcsection——图标、版本信息全空。根因MSVC 链接器做 Dead Code EliminationDCEresource.lib 里只有资源 section没有任何被代码引用的符号 → 直接跳过。修复把winres 0.1换成winresource 0.1同一维护者的活跃分支它正确处理了链接问题。 坑 4模态遮罩挡住弹窗内所有交互现象分类下拉框打不开、保存按钮点不了、密码改不了。根因模态遮罩半透明黑色层绘制在和弹窗同一个Order::Middle层级上它盖住了弹窗内部的交互控件。修复直接移除模态遮罩。保持单窗口模态约束即可任意时刻只显示一个弹窗视觉上的模态感不用遮罩也能达到。四、功能速览密码记录 名称 / 分类 / 账号 / 密码 / 手机号 / 网址 / 备注️ 自定义分类工作/生活/金融/购物…️ 软删除回收站可恢复 / 彻底删除 网址一键打开浏览器工具箱密码生成器长度 8-128可配置字符集排除易混字符加密备份/恢复导出 .pbk 文件主密码保护CSV 导入Chrome / Edge / Firefox 导出格式一键导入️安全检查弱口令扫描、重复口令检测、空密码提醒系统集成 关闭退托盘常驻零打扰⚙️ 深色 / 浅色主题Windows 主题色自动跟随 空闲自动锁定可配置超时秒数 可选开机自启五、安全设计清单设计点说明✅ AES-256-GCM对称加密 认证防篡改✅ PBKDF2 60 万次抗暴力破解✅ 每条记录独立 Nonce Salt防止彩虹表相同明文密文不同✅ 零明文驻留内存锁定态自动擦除敏感缓冲区✅ SQLite 完整性校验防断电损坏✅ 所有写入事务包裹失败自动回滚✅ 错误密码锁定连续错误 5 次锁定 30 秒✅ 备份前强制验证主密码防止被植入木马自动导出✅ 日志不含敏感数据托盘日志只有状态永远不写密码✅ 纯离线启动后零网络请求六、数据存哪了就一个文件%APPDATA%\passwordbook\password.db可直接复制备份到 U 盘、云盘恢复时关闭程序再复制回来主密码不变。七、发布与下载项目链接GitHubhttps://github.com/jeokeo011222/passwordbookRelease 下载v1.0.0 zip 包解压即用无需安装系统要求Windows 10/11 (x64)单文件 ~7 MB协议PolyForm Noncommercial License 1.0.0从源码构建git clone https://github.com/jeokeo011222/passwordbook.git cd passwordbook cargo build --release # 产物target/release/passwordbook.exe八、结语密码本这种东西你不写自己的就只能相信别人的。虽然很多开源密码管理器做得比这个好但自己亲手写一个的过程让我对加密、GUI 框架、Windows 消息泵、资源注入这些东西有了更底层的理解。那些踩过的坑每一个都让我对 Rust 生态的某一角有了更清晰的认识。如果你也是 Rust 爱好者或者想动手写点什么强烈建议你也写一个自己的密码本。这是一个完美的练手项目——体量适中、安全要求高、涉及的技术面广、最终产物还真的能用。联系我渠道方式GitHubjeokeo011222QQ3571038944欢迎提 Issue / PR / 功能建议PS写完这篇文章我突然意识到——我花了好几个星期写了 1 万多行代码然后又花了好几个小时写了这篇 3000 字的软文可能在推广这件事上我比写代码还认真