异或加密仅适用于局部文件加密且需固定偏移,因其不改变长度、无需对齐,但无防篡改和防重放能力;必须校验文件大小、实际读取字节数及定位有效性,并以二进制模式操作。

异或加密为什么只适合“部分文件”且必须从固定偏移开始
因为异或本身不改变数据长度,也不需要填充或块对齐,直接按字节和密钥循环异或就行——这正好匹配“从某 offset 开始改一部分”的需求。但反过来说,它完全不防篡改、不防重放,更不能替代 AES 这类完整加密方案;如果你要保护的是配置文件里第 1024 字节起的 token 字段,xor 合理;如果想防逆向或传输窃听,它连门槛都算不上。
- 密钥长度任意,但建议用
std::vector<uint8_t></uint8_t>存,别用 C 风格字符串(末尾\0会截断) - 偏移量
offset和长度len必须在文件大小范围内,否则读写越界——std::ifstream::seekg不报错,但后续read()可能返回 0 字节 - 务必用二进制模式打开文件:
std::ios::binary,否则 Windows 下\r\n转换会破坏原始字节流
如何安全地读取+修改文件指定区域(C++ ifstream / ofstream)
不能先读整个文件再改再写回——大文件耗内存,且“只改中间一段”时没必要。正确做法是:以 inout 模式打开,seekp 定位,write 覆盖。但注意:fstream 的 in | out 模式默认不支持直接覆盖(某些平台会清空文件),稳妥方案是分开读写,或用 in | out | binary + seekg/seekp 同步。
- 先用
std::ifstream以binary模式读取原始数据块(从offset开始,len字节) - 对这块内存做
xor运算(密钥逐字节循环,用key[i % key.size()]) - 再用
std::ofstream以binary | in | out模式打开,seekp(offset),write()写回 - 别忘了检查
is_open()和good(),尤其seekp后要确认tellp() == offset,避免定位失败却静默继续
常见错误:密钥循环错位、字节序混淆、文件截断
最常踩的坑不是算法逻辑,而是 I/O 边界处理。比如用 std::string 当密钥,里面混入 \0,size() 返回值就错了;或者把 offset 当成行号传进去,结果 seek 到了错误位置;还有人用 ofstream 默认构造打开文件,没加 ios::in,导致文件被清空。
-
std::string key = "\x01\x02\x00\x03"→ 实际长度是 3(\0截断),但你想用 4 字节密钥 → 改用std::vector<uint8_t>{0x01,0x02,0x00,0x03}</uint8_t> - 写入前没调
flush()或没关闭流,系统缓存未落盘,下次读还是旧数据 - 目标文件原本只有 1000 字节,你设
offset=500, len=800→ 实际只能读到 500 字节,但代码仍对 800 字节做 xor → 后 300 字节是栈垃圾值 → 写回去就损坏文件
C++ 示例:只加密文件第 2048 字节起的 128 字节
下面这段代码只做一件事:定位、读、xor、写回。没有封装类,不依赖第三方库,所有路径、偏移、密钥都硬编码便于对照逻辑。
立即学习“C++免费学习笔记(深入)”;
#include <fstream>
#include <vector>
#include <iostream>
<p>int main() {
const std::string path = "config.bin";
const size_t offset = 2048;
const size_t len = 128;
const std::vector<uint8_t> key = {0xDE, 0xAD, 0xBE, 0xEF};</p><pre class='brush:php;toolbar:false;'>std::ifstream fin(path, std::ios::binary);
if (!fin.is_open()) { std::cerr << "open fail\n"; return 1; }
fin.seekg(0, std::ios::end);
size_t file_size = fin.tellg();
if (offset > file_size || len > file_size - offset) {
std::cerr << "range out of bounds\n"; return 1;
}
fin.seekg(offset);
std::vector<uint8_t> buf(len);
fin.read(reinterpret_cast<char*>(buf.data()), len);
if (fin.gcount() != static_cast<std::streamsize>(len)) {
std::cerr << "read short\n"; return 1;
}
for (size_t i = 0; i < buf.size(); ++i) {
buf[i] ^= key[i % key.size()];
}
std::ofstream fout(path, std::ios::binary | std::ios::in | std::ios::out);
if (!fout.is_open()) { std::cerr << "reopen fail\n"; return 1; }
fout.seekp(offset);
fout.write(reinterpret_cast<const char*>(buf.data()), len);
fout.close();
return 0;}
关键点就三个:file_size 校验、gcount() 检查实际读取字节数、seekp 后不假设一定成功。其他都是围绕它们服务的防御性写法。
真正难的不是 xor 本身,是确保你操作的那几 KB 确实是你以为的那几 KB——磁盘、缓存、文件系统、C++ 流状态,任何一环掉链子,加密就变成误伤。



















