rust 实现文件加解密

2024.06.09

本方法使用了 aes-gcmpbkdf2 库,其中 aes-gcm 用来加密文件, pbkdf2 用来生成加密密钥

附上整个文件加解密类的实现代码: 依赖:

toml
aes-gcm = "0.10"
pbkdf2 = { version = "0.12", features = ["simple"] }
sha2 = "0.10.8"

代码:

rs
pub struct FileEncryption<'cipher> {
  password: &'cipher str,
  rounds: u32,
}

impl<'cipher> FileEncryption<'cipher> {
  pub fn new(password: &'cipher str, rounds: u32) -> Self {
    FileEncryption {
      password,
      rounds,
    }
  }

  fn create_password(&self, file_name: &str) -> Result<[u8; 32]> {
    let salt = file_name.as_bytes();
    let password = self.password.as_bytes();
    let mut key = [0u8; 32];
    pbkdf2_hmac::<Sha256>(password, salt, self.rounds, &mut key);
    Ok(key)
  }

  fn generate_cipher_and_nonce(&self, file_name: &str)
                               -> Result<(AesGcm<Aes256, U12>, GenericArray<u8, U12>)> {
    let key = self.create_password(file_name)?;
    let key = Key::<Aes256Gcm>::from(key);
    let cipher = Aes256Gcm::new(&key);
    let mut nonce = Nonce::default();
    pbkdf2_hmac::<Sha256>(file_name.as_bytes(), self.password.as_bytes(), self.rounds, &mut nonce);
    Ok((cipher, nonce))
  }

  pub fn encrypt_file(&self, file_path: &Path) -> Result<PathBuf> {
    let file_name = file_path.file_name().ok_or(anyhow!(CANNOT_READ_FILE_NAME))?
      .to_str().ok_or(anyhow!(CONVERT_STR_FAIL))?;
    let (cipher, nonce) =
      self.generate_cipher_and_nonce(file_name)?;
    let mut input_file = File::open(file_path)?;
    let mut buffer = Vec::new();
    input_file.read_to_end(&mut buffer)?;
    let ciphertext = cipher.encrypt(&nonce, &*buffer);
    if let Err(e) = ciphertext {
      anyhow!(e);
    };
    let ciphertext = ciphertext.unwrap();
    let save_path = PathBuf::new().join(TEMP_PATH)
      .join(format!("{}{}", file_name, ENCRYPTED_FILE_SUFFIX));
    let mut output_file = File::create(&save_path)?;
    output_file.write_all(&*ciphertext)?;
    Ok(save_path)
  }

  pub fn decrypt_file(&self, file_path: &Path) -> Result<PathBuf> {
    let file_name = file_path.file_name().ok_or(anyhow!(CANNOT_READ_FILE_NAME))?
      .to_str().ok_or(anyhow!(CONVERT_STR_FAIL))?.replace(ENCRYPTED_FILE_SUFFIX, "");
    let (cipher, nonce) = self.generate_cipher_and_nonce(&*file_name)?;
    let mut input_file = File::open(file_path)?;
    let mut buffer = Vec::new();
    input_file.read_to_end(&mut buffer)?;
    let ciphertext = cipher.decrypt(&nonce, &*buffer);
    if let Err(e) = ciphertext {
      anyhow!(e);
    };
    let ciphertext = ciphertext.unwrap();
    let save_path = PathBuf::new().join(TEMP_PATH).join(file_name);
    let mut output_file = File::create(&save_path)?;
    output_file.write_all(&*ciphertext)?;
    Ok(save_path)
  }
}

大写的常量都是其他文件里定义的 &str 类型