当前位置: 首页 > news >正文

Rust 命令行密码管理器工具开发

文章目录

    • 一、项目概述
      • 1.1 项目目标
      • 1.2 核心功能
      • 1.3 技术栈清单
    • 二、项目初始化
      • 2.1 创建项目
      • 2.2 配置 Cargo.toml
    • 三、核心模块实现
      • 3.1 主程序(src/main.rs)
      • 3.2 密码管理器模块(_src/password_manager.rs_)
      • 3.3 加密模块( _src/crypto.rs_)
      • 3.4 创建数据模型(_src/models.rs_)
      • 3.5 创建 lib.rs 文件
    • 四、测试与使用
      • 4.1 子命令详解
      • 4.2 测试
    • 五、项目总结

一、项目概述

1.1 项目目标

开发一款本地存储、安全加密的命令行密码管理工具,支持密码的添加、查询、修改、删除,以及高强度随机密码生成,核心满足 “隐私安全”“本地可控”“操作便捷” 三大需求。

1.2 核心功能

  1. 隐私安全
    1. 使用 AES-256-CBC 加密算法加密存储所有密码
    2. 使用 PBKDF2 算法将主密码转换为加密密钥
    3. 100,000 轮密钥派生增强安全性
    4. 内存中的敏感数据使用 zeroize 安全清除
  2. 本地可控
    1. 所有数据本地存储,不依赖网络
    2. 数据文件使用加密格式存储
    3. 可以完全控制数据存储位置
  3. 操作便捷
    1. 命令行界面,支持多种操作
    2. 自动生成高强度随机密码
    3. 支持将密码复制到剪贴板
    4. 支持按服务和用户名查询

1.3 技术栈清单

依赖库版本要求用途说明
clap>=4.0命令行参数解析(定义 init/add/search 等子命令及选项)
aes>=0.8AES-256 加密算法核心实现,提供对称加密能力
cbc>=0.1支持 AES 算法的 CBC 加密模式,确保数据块关联性
pbkdf2>=0.12PBKDF2 密钥派生函数,从用户主密码生成高强度加密密钥(结合 SHA-256)
sha2>=0.10SHA-256 哈希算法,用于 PBKDF2 密钥派生的哈希环节
serde>=1.0数据序列化 / 反序列化框架(需启用 derive 特性,用于 JSON 处理)
serde_json>=1.0JSON 格式处理,实现密码数据的序列化存储与反序列化读取
rand>=0.8生成高强度随机密码(控制字符类型、长度)及安全随机数(如加密盐值)
clipboard>=0.5跨平台剪贴板操作,支持密码一键复制到剪贴板
rpassword>=7.2安全读取密码输入(隐藏终端输入字符,避免明文泄露)
dirs>=4.0获取系统默认配置目录,用于存储加密数据文件(跨平台适配)
thiserror>=1.0自定义错误类型,统一错误处理逻辑(如加密错误、文件错误、参数错误)
chrono>=0.4时间处理库,记录密码条目创建 / 更新时间、加密文件最后修改时间
colored>=2.0终端输出美化(成功信息标绿、错误信息标红、提示信息标蓝)
zeroize>=1.6敏感数据内存清零,避免密码、密钥等信息在内存中残留

二、项目初始化

2.1 创建项目

# 创建项目目录
cargo new rust-pass-manager
cd rust-pass-manager

2.2 配置 Cargo.toml

[package]
name = "secure-password-manager"
version = "0.1.0"
edition = "2021"[dependencies]
clap = { version = "4.0", features = ["derive"] }
aes = "0.8"
cbc = { version = "0.1", features = ["alloc"] }
pbkdf2 = "0.12"
rand = "0.8"
sha2 = "0.10"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
rpassword = "7.2"
clipboard = "0.5"
zeroize = "1.6"

三、核心模块实现

3.1 主程序(src/main.rs)

// src/main.rs
use clap::Parser;
use std::path::Path;
use std::fs;
use std::io::{self, Write};mod password_manager;
mod crypto;
mod models;use password_manager::PasswordManager;
use models::{PasswordEntry, Config};/// 安全密码管理器 - 本地存储、加密保护
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {/// 要执行的操作#[command(subcommand)]command: Command,
}#[derive(clap::Subcommand, Debug)]
enum Command {/// 添加新密码Add {/// 服务名称service: String,/// 用户名username: String,/// 密码(如果未提供将生成随机密码)#[arg(short, long)]password: Option<String>,/// 密码长度(用于生成随机密码)#[arg(short = 'l', long, default_value = "16")]length: usize,},/// 查询密码Get {/// 服务名称service: String,/// 用户名#[arg(short, long)]username: Option<String>,/// 是否复制到剪贴板#[arg(short, long)]copy: bool,},/// 列出所有服务List,/// 修改密码Update {/// 服务名称service: String,/// 用户名username: String,/// 新密码#[arg(short, long)]password: Option<String>,/// 密码长度(用于生成随机密码)#[arg(short = 'l', long, default_value = "16")]length: usize,},/// 删除密码Delete {/// 服务名称service: String,/// 用户名#[arg(short, long)]username: Option<String>,},/// 生成随机密码Generate {/// 密码长度#[arg(short, long, default_value = "16")]length: usize,/// 是否包含特殊字符#[arg(short, long)]special: bool,/// 是否复制到剪贴板#[arg(short, long)]copy: bool,},/// 初始化密码库Init,
}fn main() -> Result<(), Box<dyn std::error::Error>> {let args = Args::parse();// 检查是否已初始化let config_path = "passwords.config";let data_path = "passwords.data";match &args.command {Command::Init => {init_password_manager(config_path)?;return Ok(());}_ => {if !Path::new(config_path).exists() {println!("❌ 密码库尚未初始化,请先运行: secure-password-manager init");return Ok(());}}}// 获取主密码let master_password = rpassword::prompt_password("请输入主密码: ")?;// 初始化密码管理器let mut pm = PasswordManager::new(master_password, config_path, data_path)?;// 执行命令match &args.command {Command::Add { service, username, password, length } => {let password = match password {Some(pwd) => pwd.clone(),None => crypto::generate_password(*length, true),};let entry = PasswordEntry {service: service.clone(),username: username.clone(),password,created_at: chrono::Utc::now(),updated_at: chrono::Utc::now(),};pm.add_password(entry)?;println!("✅ 密码添加成功");}Command::Get { service, username, copy } => {let entries = pm.get_passwords(service, username.as_deref())?;if entries.is_empty() {println!("📭 未找到相关密码");return Ok(());}for entry in &entries {println!("服务: {}", entry.service);println!("用户名: {}", entry.username);if *copy {#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]{match clipboard::write(entry.password.as_str()) {Ok(_) => println!("📋 密码已复制到剪贴板"),Err(_) => println!("密码: {}", entry.password),}}#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]{println!("密码: {}", entry.password);}} else {println!("密码: {}", entry.password);}println!("创建时间: {}", entry.created_at.format("%Y-%m-%d %H:%M:%S"));println!("更新时间: {}", entry.updated_at.format("%Y-%m-%d %H:%M:%S"));println!("-------------------------");}}Command::List => {let services = pm.list_services()?;if services.is_empty() {println!("📭 暂无密码记录");return Ok(());}println!("🔐 已保存的服务:");for service in services {println!("- {}", service);}}Command::Update { service, username, password, length } => {let password = match password {Some(pwd) => pwd.clone(),None => crypto::generate_password(*length, true),};pm.update_password(service, username, password)?;println!("✅ 密码更新成功");}Command::Delete { service, username } => {let deleted = pm.delete_password(service, username.as_deref())?;if deleted > 0 {println!("✅ 删除了 {} 条记录", deleted);} else {println!("📭 未找到要删除的记录");}}Command::Generate { length, special, copy } => {let password = crypto::generate_password(*length, *special);if *copy {#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]{match clipboard::write(password.as_str()) {Ok(_) => println!("📋 生成的密码已复制到剪贴板: {}", password),Err(_) => println!("生成的密码: {}", password),}}#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]{println!("生成的密码: {}", password);}} else {println!("生成的密码: {}", password);}}Command::Init => unreachable!(),}Ok(())
}fn init_password_manager(config_path: &str) -> Result<(), Box<dyn std::error::Error>> {if Path::new(config_path).exists() {println!("⚠️  密码库已存在,是否要重新初始化?(y/N): ");let mut input = String::new();io::stdin().read_line(&mut input)?;if !input.trim().eq_ignore_ascii_case("y") {println!("❌ 初始化已取消");return Ok(());}}let master_password = rpassword::prompt_password("请设置主密码: ")?;let confirm_password = rpassword::prompt_password("请确认主密码: ")?;if master_password != confirm_password {println!("❌ 两次输入的密码不一致");return Ok(());}let salt = crypto::generate_salt();let config = Config { salt };fs::write(config_path, serde_json::to_string(&config)?)?;fs::write("passwords.data", "")?;println!("✅ 密码库初始化成功!");println!("🔑 请牢记您的主密码,丢失将无法恢复数据!");Ok(())
}

3.2 密码管理器模块(src/password_manager.rs)

// src/password_manager.rs
use std::collections::HashMap;
use std::fs;
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};use crate::crypto;
use crate::models::{PasswordEntry, Config};pub struct PasswordManager {master_key: Vec<u8>,config: Config,data_path: String,passwords: HashMap<String, Vec<PasswordEntry>>,
}impl PasswordManager {pub fn new(master_password: String,config_path: &str,data_path: &str,) -> Result<Self, Box<dyn std::error::Error>> {// 读取配置let config_content = fs::read_to_string(config_path)?;let config: Config = serde_json::from_str(&config_content)?;// 生成主密钥let master_key = crypto::derive_key(&master_password, &config.salt);// 初始化实例let mut pm = PasswordManager {master_key,config,data_path: data_path.to_string(),passwords: HashMap::new(),};// 加载现有数据pm.load_data()?;Ok(pm)}fn load_data(&mut self) -> Result<(), Box<dyn std::error::Error>> {let data = fs::read(&self.data_path)?;if data.is_empty() {return Ok(());}let decrypted_data = crypto::decrypt(&data, &self.master_key)?;self.passwords = serde_json::from_slice(&decrypted_data)?;Ok(())}fn save_data(&self) -> Result<(), Box<dyn std::error::Error>> {let json_data = serde_json::to_vec(&self.passwords)?;let encrypted_data = crypto::encrypt(&json_data, &self.master_key)?;fs::write(&self.data_path, encrypted_data)?;Ok(())}pub fn add_password(&mut self, entry: PasswordEntry) -> Result<(), Box<dyn std::error::Error>> {self.passwords.entry(entry.service.clone()).or_insert_with(Vec::new).push(entry);self.save_data()?;Ok(())}pub fn get_passwords(&self,service: &str,username: Option<&str>,) -> Result<Vec<PasswordEntry>, Box<dyn std::error::Error>> {let mut results = Vec::new();if let Some(entries) = self.passwords.get(service) {for entry in entries {if let Some(target_username) = username {if entry.username == target_username {results.push(entry.clone());}} else {results.push(entry.clone());}}}Ok(results)}pub fn list_services(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {let mut services: Vec<String> = self.passwords.keys().cloned().collect();services.sort();Ok(services)}pub fn update_password(&mut self,service: &str,username: &str,new_password: String,) -> Result<(), Box<dyn std::error::Error>> {if let Some(entries) = self.passwords.get_mut(service) {for entry in entries {if entry.username == username {entry.password = new_password;entry.updated_at = Utc::now();self.save_data()?;return Ok(());}}}// 如果没有找到,添加新条目let entry = PasswordEntry {service: service.to_string(),username: username.to_string(),password: new_password,created_at: Utc::now(),updated_at: Utc::now(),};self.add_password(entry)?;Ok(())}pub fn delete_password(&mut self,service: &str,username: Option<&str>,) -> Result<usize, Box<dyn std::error::Error>> {let mut deleted_count = 0;if let Some(username) = username {// 删除特定用户名的条目if let Some(entries) = self.passwords.get_mut(service) {let original_len = entries.len();entries.retain(|entry| entry.username != username);deleted_count = original_len - entries.len();// 如果服务下没有条目了,删除服务if entries.is_empty() {self.passwords.remove(service);}}} else {// 删除整个服务的所有条目if let Some(entries) = self.passwords.remove(service) {deleted_count = entries.len();}}if deleted_count > 0 {self.save_data()?;}Ok(deleted_count)}
}

3.3 加密模块( src/crypto.rs)

// src/crypto.rs
use aes::Aes256;
use cbc::{Cipher, Decryptor, Encryptor};
use pbkdf2::pbkdf2;
use rand::{Rng, rngs::OsRng};
use sha2::Sha256;
use std::num::NonZeroU32;const KEY_SIZE: usize = 32;
const IV_SIZE: usize = 16;
const SALT_SIZE: usize = 32;
const PBKDF2_ROUNDS: u32 = 100_000;pub fn generate_salt() -> Vec<u8> {let mut salt = vec![0u8; SALT_SIZE];OsRng.fill(&mut salt);salt
}pub fn derive_key(password: &str, salt: &[u8]) -> Vec<u8> {let mut key = vec![0u8; KEY_SIZE];pbkdf2::<Sha256>(password.as_bytes(),salt,NonZeroU32::new(PBKDF2_ROUNDS).unwrap(),&mut key,);key
}pub fn encrypt(data: &[u8], key: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {let mut iv = vec![0u8; IV_SIZE];OsRng.fill(&mut iv);let cipher = Encryptor::<Aes256>::new(key.into(), &iv);let mut buffer = data.to_vec();cipher.encrypt_padded_mut::<cbc::cipher::block_padding::Pkcs7>(&mut buffer, data.len())?;let mut result = iv;result.extend_from_slice(&buffer);Ok(result)
}pub fn decrypt(encrypted_data: &[u8], key: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {if encrypted_data.len() < IV_SIZE {return Err("Invalid encrypted data".into());}let (iv, ciphertext) = encrypted_data.split_at(IV_SIZE);let cipher = Decryptor::<Aes256>::new(key.into(), iv.into());let mut buffer = ciphertext.to_vec();let decrypted_len = cipher.decrypt_padded_mut::<cbc::cipher::block_padding::Pkcs7>(&mut buffer)?;buffer.truncate(decrypted_len);Ok(buffer)
}pub fn generate_password(length: usize, with_special: bool) -> String {let charset = if with_special {"ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz\0123456789\!@#$%^&*()_+-=[]{}|;:,.<>?"} else {"ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz\0123456789"};let mut rng = OsRng;(0..length).map(|_| {let idx = rng.gen_range(0..charset.len());charset.chars().nth(idx).unwrap()}).collect()
}

3.4 创建数据模型(src/models.rs)

// src/models.rs
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PasswordEntry {pub service: String,pub username: String,pub password: String,#[serde(with = "chrono::serde::ts_seconds")]pub created_at: DateTime<Utc>,#[serde(with = "chrono::serde::ts_seconds")]pub updated_at: DateTime<Utc>,
}#[derive(Serialize, Deserialize, Debug)]
pub struct Config {pub salt: Vec<u8>,
}

3.5 创建 lib.rs 文件

// src/lib.rs
pub mod password_manager;
pub mod crypto;
pub mod models;

四、测试与使用

4.1 子命令详解

初始化密码库

cargo run -- init

  • 功能:初始化密码库,创建配置文件和数据文件
  • 参数:无
  • 选项:无
  • 说明:首次使用必须执行此命令

添加密码

cargo run -- add <service> <username> [OPTIONS]

  • 功能:添加新密码记录
  • 参数
    • <service> - 服务名称(必填),如 github、gmail、jira 等
    • <username> - 用户名(必填),如 user@example.com、john_dev 等
  • 选项
    • -p, --password <password> - 指定具体密码内容
    • -l, --length <length> - 指定生成密码长度(默认16位)
  • 说明:如果未指定密码,则自动生成高强度密码

查询密码

cargo run -- get <service> [OPTIONS]

  • 功能:查询密码记录
  • 参数
    • <service> - 服务名称(必填)
  • 选项
    • -u, --username <username> - 指定用户名进行精确查询
    • -c, --copy - 将密码复制到剪贴板
  • 说明:如不指定用户名,将显示该服务下的所有账户

列出所有服务

cargo run -- list

  • 功能:列出所有已保存的服务名称
  • 参数:无
  • 选项:无
  • 说明:按字母顺序显示所有服务

更新密码

cargo run -- update <service> <username> [OPTIONS]

  • 功能:更新现有密码记录
  • 参数
    • <service> - 服务名称(必填)
    • <username> - 用户名(必填)
  • 选项
    • -p, --password <password> - 指定新的密码内容
    • -l, --length <length> - 指定生成新密码的长度(默认16位)
  • 说明:如果未指定密码,则自动生成新的高强度密码

删除密码

cargo run -- delete <service> [OPTIONS]

  • 功能:删除密码记录
  • 参数
    • <service> - 服务名称(必填)
  • 选项
    • -u, --username <username> - 指定要删除的用户名
  • 说明:如不指定用户名,将删除该服务下的所有账户记录

生成随机密码

cargo run -- generate [OPTIONS]

  • 功能:生成高强度随机密码
  • 参数:无
  • 选项
    • -l, --length <length> - 指定密码长度(默认16位)
    • -s, --special - 包含特殊字符
    • -c, --copy - 将生成的密码复制到剪贴板
  • 说明:仅生成密码,不保存到密码库

4.2 测试

  1. 初始化密码库:
cargo run -- init

我们设置密码为123456789

  1. 添加密码:
cargo run -- add github myusername

  1. 查询密码:
cargo run -- get github --username myusername --copy

当我们粘贴时就可以看见密码

  1. 列出所有服务:
cargo run -- list

  1. 更新密码:
cargo run -- update github myusername

  1. 删除密码:
cargo run -- delete github --username myusername

  1. 生成随机密码:
cargo run -- generate --length 20 --special

五、项目总结

该项目成功实现了一个功能完整、安全可靠的命令行密码管理工具。通过采用现代加密技术和 Rust 语言的安全特性,确保了用户密码数据的高度安全性。同时,友好的命令行界面和丰富的功能使得密码管理变得更加便捷高效。

该工具不仅满足了基本的密码存储需求,还提供了密码生成、剪贴板集成等实用功能,真正实现了"隐私安全"、"本地可控"和"操作便捷"的设计目标。无论是个人用户还是企业用户,都可以通过该工具更好地管理自己的密码资产,提升整体的数字安全水平。

想了解更多关于Rust语言的知识及应用,可前往旋武开源社区(https://xuanwu.openatom.cn/),了解更多资讯~

http://www.dtcms.com/a/618939.html

相关文章:

  • Restful协议举例,主要作用
  • h5网站开发流程图保山市住房和城乡建设局门户网站
  • 如何查找网站死链南京制作网站多少钱
  • 学习Ansible Playbook 核心语法
  • flink CDC 3.5.0
  • 阿里巴巴网站备案号用wordpress
  • 网站seo服务商seo文章外包
  • 微信网站设计运营用DW做的网站怎么分享给别人
  • 怎么建网站教程图解棋牌游戏开发多少钱
  • 广西智能网站建设哪家有h5页面设计是什么意思
  • AI 招聘智能体
  • 菏泽网站建设熊掌号微信怎么制作自己的小程序
  • 网站商城系统建设协会网站改版建议
  • CSS-2:CSS的元素显示模式
  • 国外互动网站wordpress使用邮箱
  • F280049C学习笔记之SCI
  • 17.背光PWM调节
  • RAID特性
  • ThreadLocal为什么会发生内存泄漏
  • 在阿里云建设一个网站的全流程华凯创意的展馆设计案例
  • 网站主页怎么做竞价排名适合百度这样的网络平台吗
  • 开源机器学习课程mlcourse.ai:理论与实践完美结合的AI学习指南
  • 网站怎么做站内美化城乡建设杂志社官方网站
  • 网站推广的主要方法腾讯云域名注册官网
  • MySQL 主从延迟问题深度解析:常见原因与解决方案(强总结 + 易懂版)
  • 【开题答辩全过程】以 基于springboot的在线影院系统设为例,包含答辩的问题和答案
  • 39.华为云运维类服务
  • 做个网站商场需要多少软件商店下载到手机
  • 【Java 基础】3 面向对象 - this
  • 网站开发赚钱方向做个app商城类的要多少钱