C++ Botan库可否将同一个文件同时用作DataSource_Stream与DataSink_Stream
问题原因
你直接指定输出为in.txt默认是追加写入,就算改成覆盖模式也会出现读写冲突:你在读取后半段明文之前,前半段写入的密文就会覆盖还没读取的明文内容,最终得到错误的加密结果。
方案1:小文件场景(内存足够容纳完整文件)
这是最稳妥的实现方式,不存在读写冲突风险:
- 先将整个文件的明文全部读取到内存中,关闭原文件的读句柄
- 在内存中完成明文加密
- 以覆盖写入模式打开原文件,将密文写入
实现代码示例:
// 二进制模式读取完整明文到内存 DataSource_Stream in("in.txt", true); secure_vector<uint8_t> plaintext = in.read_all(); in.close(); // 执行加密 Pipe pipe(get_cipher("AES-128/CTR-BE", key, iv, Cipher_Dir::Encryption)); pipe.process_msg(plaintext); secure_vector<uint8_t> ciphertext = pipe.read_all(); // 覆盖写回原文件,第二个参数false表示非追加的覆盖模式 DataSink_Stream out("in.txt", false); out.write(ciphertext); out.close();
方案2:大文件场景(无法全量加载到内存)
该方案仅适用于不需要填充的加密模式(比如你用的CTR,以及GCM、ChaCha20等流加密/流模式块加密),这类模式加密前后的数据长度完全一致,可以通过分块随机读写实现原地加密,完全不占用额外存储空间:
const size_t CHUNK_SIZE = 4096; // 可调整为和磁盘块对齐的大小,比如16KB std::fstream file("in.txt", std::ios::in | std::ios::out | std::ios::binary); if (!file.is_open()) { // 自行处理文件打开错误逻辑 } // 获取文件总大小 file.seekg(0, std::ios::end); const size_t total_size = file.tellg(); file.seekg(0, std::ios::beg); auto cipher = get_cipher("AES-128/CTR-BE", key, iv, Cipher_Dir::Encryption); size_t current_offset = 0; secure_vector<uint8_t> chunk_buf(CHUNK_SIZE); while (current_offset < total_size) { const size_t read_len = std::min(CHUNK_SIZE, total_size - current_offset); // 读当前块明文 file.read(reinterpret_cast<char*>(chunk_buf.data()), read_len); // 加密当前块 cipher->process(chunk_buf.data(), chunk_buf.data(), read_len); // 跳回当前块起始位置写入密文 file.seekp(current_offset); file.write(reinterpret_cast<const char*>(chunk_buf.data()), read_len); // 跳到下一个块的读位置 current_offset += read_len; file.seekg(current_offset); } file.close();
注意事项
- 如果使用CBC、ECB等需要填充的块加密模式,密文长度会比明文多一个块大小,无法使用该原地加密方案
- 建议操作前备份重要文件,避免中途程序崩溃、掉电导致文件不可逆损坏
- 如果需要做完整性校验,建议先计算明文的MAC值,加密完成后将MAC存在文件末尾或者单独的存储位置
内容的提问来源于stack exchange,提问作者EvilTrtl
相关产品推荐
相关产品推荐

