You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Aes128Cbc解密消息?使用block-modes报BlockModeError如何解决

问题原因与修复方案

核心报错原因

BlockModeError在CBC模式下解密触发,通常是三类原因:

  • 待解密的密文被篡改、不完整,或者长度不是AES块大小(16字节)的整数倍
  • 解密使用的密钥、IV和加密时不一致
  • 密文填充校验失败

代码中存在的具体问题

  1. 文件读取指针位置错误
    在new方法中用append(true)打开文件,此时文件指针默认位于文件末尾。调用read_details时直接执行read操作,只会读到空内容,初始化的buffer虽然长度和文件大小一致,但实际有效读取的字节数为0,剩余都是初始的0值,完全不是实际存储的密文,解密必然失败。
  2. 多次追加加密内容无法一次性解密
    write方法每次调用都会用固定全0IV加密当前传入的字符串,然后追加到文件末尾。如果多次调用write,文件中的密文是多段独立加密的结果拼接而成,CBC模式是链式加密,一次性读取整段拼接的密文解密时,块的上下文不匹配,也会触发解密错误。
  3. (可选优化项)写密文时循环单字节写入效率很低,可直接把encrypt_vec返回的整个向量一次性写入文件。

修复代码参考

use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use aes::Aes128;
use std::fs::{File, OpenOptions, metadata};
use std::io::{Seek, SeekFrom, Write, Read};
use std::path::PathBuf;
use directories::UserDirs;

type Aes128Cbc = Cbc<Aes128, Pkcs7>;

pub struct FileHandler{
    file: File,
    file_path: String,
    // 原变量名存在拼写错误,已修正为encryption_key
    encryption_key: [u8; 16],
}

impl FileHandler{
    pub fn new() -> FileHandler{
        let mut temp_dir = PathBuf::from(UserDirs::new().unwrap().document_dir().unwrap());
        temp_dir.push("storage.txt");
        let temp_file: File;

        match OpenOptions::new().append(true).read(true).open(&temp_dir){
            Ok(result) => {temp_file = result},
            Err(_) => { 
                temp_file = File::create(&temp_dir).expect(&format!("Could not create file! - {}", temp_dir.to_str().unwrap())); 
            }
        }

        FileHandler {
            file: temp_file,
            file_path: String::from(temp_dir.to_str().unwrap()),
            // 全0密钥仅为示例,实际使用请替换为安全生成的密钥
            encryption_key: [0u8; 16],
        }
    }

    fn write(&mut self, string: &str){  
        let iv_buffer = [0u8; 16];
        let cipher = Aes128Cbc::new_from_slices(&self.encryption_key, &iv_buffer).unwrap();
        let encrypted = cipher.encrypt_vec(string.as_bytes());
        // 写入密文前先写入密文长度,方便后续分段读取解密
        self.file.write_all(&(encrypted.len() as u32).to_be_bytes()).expect("Failed to write length");
        self.file.write_all(&encrypted).expect("Failed to write encrypted data");
        self.file.flush().unwrap();
    }

    pub fn read_details(&mut self) -> Option<String> {
        // 读取前先将文件指针移动到文件开头
        self.file.seek(SeekFrom::Start(0)).unwrap();

        let metadata = metadata(&self.file_path).unwrap();
        let mut buffer = vec![0; metadata.len() as usize];
        // 用read_exact确保读满整个缓冲区
        self.file.read_exact(&mut buffer).expect("Failed to read file");

        let iv_buffer = [0u8; 16];
        let cipher = Aes128Cbc::new_from_slices(&self.encryption_key, &iv_buffer).unwrap();

        // 若为单次写入的内容可直接解密,多次写入需按之前写入的长度分段解密
        let decrypted = cipher.decrypt_vec(&buffer).unwrap();
        Some(String::from_utf8(decrypted).unwrap())
    }
}

额外注意事项

  • 生产环境不要使用固定全0IV,每次加密应生成随机IV,和密文一起存储到文件中,解密时读取对应IV使用
  • 密钥不要硬编码在代码中,建议通过安全的配置方式读取
  • 如果需要多次写入内容,解密时需要按写入前记录的密文长度,分段解密每一段独立加密的内容

内容的提问来源于stack exchange,提问作者Jack F

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 21:54:04