AES解密结果末尾出现随机字符的问题及代码修正方案
AES解密后末尾出现多余字符的问题解决及代码优化
问题根源
解密后末尾出现多余字符,核心原因是禁用了AES的自动填充(PaddingMode.None)。AES属于块加密算法,要求明文长度必须是块大小(16字节)的整数倍。当明文长度不足时,加密过程会自动补全到块大小的倍数,解密后这些补全内容就成了多余的乱码字符。
核心修改方案
恢复AES默认的PKCS7填充模式(行业标准安全填充方式),移除代码中禁用填充的语句即可解决问题。
修改后的加密方法
public static byte[] Encrypt(int key, string plainText, int iv) { byte[] encryptedText; // 使用using包裹Aes实例,避免资源泄漏 using (Aes aesAlg = Aes.Create()) { string keyString = key.ToString(); Rfc2898DeriveBytes sourceDeriveBytes = new(keyString, Encoding.UTF8.GetBytes(keyString)); byte[] keyBytes = sourceDeriveBytes.GetBytes(16); aesAlg.Key = keyBytes; string ivString = iv.ToString(); Rfc2898DeriveBytes zendeskDeriveBytes = new(ivString, Encoding.UTF8.GetBytes(ivString)); byte[] ivBytes = zendeskDeriveBytes.GetBytes(16); aesAlg.IV = ivBytes; // 移除PaddingMode.None,使用默认的PKCS7填充 ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt, Encoding.UTF8)) { swEncrypt.Write(plainText); } } encryptedText = msEncrypt.ToArray(); } } return encryptedText; }
修改后的解密方法
public static string Decrypt(int key, byte[] encryptedText, int iv) { string plainText; // 使用using包裹Aes实例,避免资源泄漏 using (Aes aesAlg = Aes.Create()) { string keyString = key.ToString(); Rfc2898DeriveBytes pbkdf = new(keyString, Encoding.UTF8.GetBytes(keyString)); byte[] keyBytes = pbkdf.GetBytes(16); aesAlg.Key = keyBytes; string ivString = iv.ToString(); Rfc2898DeriveBytes zendeskDeriveBytes = new(ivString, Encoding.UTF8.GetBytes(ivString)); byte[] ivBytes = zendeskDeriveBytes.GetBytes(16); aesAlg.IV = ivBytes; // 移除PaddingMode.None,使用默认的PKCS7填充 ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msDecrypt = new MemoryStream(encryptedText)) { using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt, Encoding.UTF8)) { plainText = srDecrypt.ReadToEnd(); } } } } return plainText; }
代码中的不良实践及改进建议
- 密钥/IV生成不安全:
- 用同一个字符串作为
Rfc2898DeriveBytes的密码和盐,等于没有加盐,大幅降低密钥安全性。正确做法是生成随机盐,将盐与加密数据一同存储/传输,解密时复用该盐。 - 用int类型的key和iv转字符串生成密钥/IV,密钥空间太小,易被暴力破解。建议直接使用足够长度的随机字节作为密钥,或用强密码配合随机盐生成密钥。
- 用同一个字符串作为
- 资源泄漏问题:原代码未用
using包裹Aes实例,修改后的代码已补上,确保资源正确释放。 - 编码显式化:修改后的代码显式指定
Encoding.UTF8,避免环境编码差异导致的明文乱码问题。
验证效果
修改完成后,解密过程会自动识别并移除PKCS7填充的多余字节,返回的明文将不再包含末尾的乱码字符,比如之前的TEST...会恢复为干净的TEST。
内容的提问来源于stack exchange,提问作者NomadsThings
相关产品推荐
相关产品推荐

