异或加密适合简单场景因其可逆、零依赖、无性能开销,但缺乏混淆与扩散,非真正加密;密钥须为固定长度byte[],避免UTF-8编码问题,加解密函数相同且逐字节异或。
异或加密为什么适合简单场景
因为异或(^)是可逆的位运算,a ^ b ^ b == a,不需要密钥管理、不依赖外部库、无性能开销。但这也意味着它**不是真正意义上的加密**——没混淆、没扩散、密钥一泄露就全破。只适用于临时保护配置文件、避免明文被一眼看穿这类需求。
常见错误现象:System.Text.Encoding.UTF8.GetBytes("密钥") 直接当字节密钥用 → 中文字符会变成多字节,导致解密错位;用 string 拼接密钥和内容再转字节 → 编码不一致,加解密字节长度对不上。
实操建议:
- 密钥必须是固定长度的
byte[],推荐用 16 或 32 字节的随机值,别用字符串硬编码 - 如果非要用字符串当密钥,统一用
Encoding.ASCII.GetBytes(),避免 UTF-8 的变长问题 - 异或操作必须按字节逐位进行,不能对整个字符串做
^(C# 不支持 string 间异或)
C# 中用 XorEncrypt 函数实现加解密
没有内置的 XorEncrypt,得自己写。核心就是遍历输入字节数组,每个字节跟密钥对应位置字节异或(密钥循环使用)。注意:加密和解密函数完全一样,传入原文或密文都行。
示例代码(精简版):
public static byte[] XorEncrypt(byte[] data, byte[] key)
{
var result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}
return result;
}使用场景:读取 ini 配置文件前解密、保存用户本地 token 前加密。
容易踩的坑:
-
key.Length == 0会导致i % 0抛DivideByZeroException - 把
result[i] = data[i] ^ key[i]写成key[i]而不是key[i % key.Length]→ 密钥短于数据时直接越界 - 忘记将结果转回
string:解密后需用Encoding.UTF8.GetString(decryptedBytes),且原始文本必须是 UTF-8 编码
文件级异或加解密怎么避免损坏
直接对整个文件流做异或最安全,不涉及编码转换。关键点是:别用 File.ReadAllText / WriteAllText,它们隐式处理编码,会破坏二进制一致性。
实操建议:
- 用
File.ReadAllBytes()和File.WriteAllBytes()处理任意文件(图片、exe、txt 都行) - 密钥不要写死在代码里,至少从配置文件或环境变量读,哪怕只是 base64 字符串
- 加解密前后校验文件长度:异或不改变长度,若
enc.Length != dec.Length,说明某处用了字符串读写
一个典型错误:File.WriteAllText(path, Encoding.UTF8.GetString(XorEncrypt(...))) → 解密时再 GetBytes 就可能因 BOM 或替换字符出错。
为什么不能用异或加密敏感数据
它抗不了最基础的已知明文攻击:比如你加密的是 JSON,攻击者知道开头是 { "token":,就能反推出密钥前几个字节;所有重复明文字节对应密文也重复,频次分析一下就露馅。
性能/兼容性影响几乎为零,但这恰恰是危险信号——真正的加密(如 Aes.Create())必须有初始化向量(IV)、填充模式、密钥派生(Rfc2898DeriveBytes),这些它全没有。
如果你的“简单”是指“不想引 NuGet 包”,那至少换 Convert.ToBase64String + 自定义偏移(比如每个字节 +7 再异或)——虽然也不安全,但比裸异或多一层心理安慰。真要存密码、API Key,别省这三分钟去配 AesGcm。
最容易被忽略的一点:异或结果可能是不可见控制字符甚至 \0,用文本编辑器打开加密后文件看到乱码不等于成功,得用十六进制查看器确认字节是否均匀分布——如果不均匀,说明密钥太短或用了弱源。










