如何使用Aes128Cbc解密消息?使用block-modes报BlockModeError如何解决
问题原因与修复方案
核心报错原因
BlockModeError在CBC模式下解密触发,通常是三类原因:
- 待解密的密文被篡改、不完整,或者长度不是AES块大小(16字节)的整数倍
- 解密使用的密钥、IV和加密时不一致
- 密文填充校验失败
代码中存在的具体问题
- 文件读取指针位置错误
在new方法中用append(true)打开文件,此时文件指针默认位于文件末尾。调用read_details时直接执行read操作,只会读到空内容,初始化的buffer虽然长度和文件大小一致,但实际有效读取的字节数为0,剩余都是初始的0值,完全不是实际存储的密文,解密必然失败。 - 多次追加加密内容无法一次性解密
write方法每次调用都会用固定全0IV加密当前传入的字符串,然后追加到文件末尾。如果多次调用write,文件中的密文是多段独立加密的结果拼接而成,CBC模式是链式加密,一次性读取整段拼接的密文解密时,块的上下文不匹配,也会触发解密错误。 - (可选优化项)写密文时循环单字节写入效率很低,可直接把
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
相关产品推荐
相关产品推荐

