直接用 Aes.Create() 加密字符串会出错,因 AES 加密字节流而非字符串,隐式编码(如 UTF-16)与解密端不一致会导致乱码或 CryptographicException;必须显式用 UTF8 编码转换。

为什么直接用 Aes.Create() 加密字符串会出错
因为 Aes 是对字节流加密,不是对字符串直接加密。你传入 string 会触发隐式编码转换,而默认编码(如 UTF-16)和解密端不一致时,解密后乱码或报 CryptographicException: The input data is not a complete block。
必须显式指定编码,并全程保持一致——推荐 UTF8,它不会因 BOM 或字节序引发歧义。
- 加密前:用
Encoding.UTF8.GetBytes(inputString)转为byte[] - 解密后:用
Encoding.UTF8.GetString(decryptedBytes)还原为字符串 - 别用
Encoding.Default,它在不同系统上可能指向 GBK/Shift-JIS,跨平台必崩
如何安全生成和复用 Key 与 IV
Key 和 IV 不能写死、不能复用、不能用 Guid.NewGuid().ToString() 这类弱熵源。AES-256 要求 Key 长度为 32 字节,IV 为 16 字节。
常见错误是把字符串直接当密钥(比如 "mySecretKey123"),这既不符合长度要求,又缺乏密码学强度。
- 生产环境:用
Rfc2898DeriveBytes(PBKDF2)从口令派生密钥,迭代次数 ≥ 100000 - 临时场景(如本地配置加密):用
RandomNumberGenerator.Fill(keyBytes)生成真随机密钥 -
IV每次加密都必须新生成,且需和密文一起传输(通常前置拼接);绝不能固定为全 0 或重复使用
为什么 Base64 编码后的密文解不开
加密后得到的是二进制 byte[],直接转成字符串(如 new string(cipherBytes))会丢失数据。必须用 Convert.ToBase64String(cipherBytes) 编码,解密时再用 Convert.FromBase64String(encoded) 还原。
常见坑是混淆了编码层级:有人把 UTF8 字符串先 Base64,再 AES 加密——这是双重编码,解密时顺序反了就永远打不开。
- 正确流程:明文 →
UTF8.GetBytes()→ AES 加密 →Convert.ToBase64String()→ 存储/传输 - 解密反向:Base64 字符串 →
Convert.FromBase64String()→ AES 解密 →UTF8.GetString()→ 明文 - 别用
Encoding.ASCII.GetString()处理密文,ASCII 会截断高位字节,导致解密失败
使用 AesGcm 还是 AesCbc
.NET 5+ 支持 AesGcm(带认证加密),它比传统 AesCbc 更安全:自动校验密文完整性,防篡改,且无需单独 HMAC。
但 AesGcm 要求 .NET 5+,且不支持 .NET Framework;如果要兼容旧框架或对接 Java/Python 的 CBC 实现,就得用 AesCbc + 显式 HMAC。
- 新项目优先选
AesGcm:它把 nonce(类似 IV)、tag(认证标签)都纳入 API,避免手搓逻辑出错 - 用
AesCbc时,务必补上 HMAC-SHA256 校验,否则存在填充预言攻击风险 - 注意:GCM 的 nonce 必须唯一且不能重复,建议用 12 字节随机值(
RandomNumberGenerator.Fill(nonce))


















