C#还原Node.js AES-128-CBC解密遇填充无效或乱码问题
问题解决:Node.js AES-128-CBC解密转C#的填充/乱码问题
核心问题分析
你的代码中密码的编码方式不一致是导致问题的关键:
- Node.js的
crypto.pbkdf2Sync接收字符串密码时,默认用UTF-8编码将其转换为字节数组 - C#的
Rfc2898DeriveBytes(string password, ...)默认用UTF-16(Unicode)编码将字符串转为字节数组
这种编码差异会导致派生的密钥完全不同,进而引发填充验证失败或解密结果乱码。此外,原C#代码中CryptoStream的处理方式也未正确触发填充逻辑。
修正后的C#解密代码
private string Decrypt(string password, byte[] data) { // 提取前16字节作为IV byte[] iv = new byte[16]; Array.Copy(data, iv, 16); // 提取去掉IV后的待解密数据 byte[] dataToDecrypt = new byte[data.Length - 16]; Array.Copy(data, 16, dataToDecrypt, 0, dataToDecrypt.Length); // 关键:用UTF-8编码转换密码,和Node.js行为对齐 byte[] passwordBytes = Encoding.UTF8.GetBytes(password); Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(passwordBytes, iv, 100, HashAlgorithmName.SHA1); using (Aes decAlg = Aes.Create()) { decAlg.Mode = CipherMode.CBC; decAlg.Padding = PaddingMode.PKCS7; // Node.js默认使用PKCS#7填充,对应C#的PKCS7 decAlg.BlockSize = 128; decAlg.Key = k2.GetBytes(16); decAlg.IV = iv; using (MemoryStream decryptionStreamBacking = new MemoryStream()) using (CryptoStream decrypt = new CryptoStream(decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write)) { decrypt.Write(dataToDecrypt, 0, dataToDecrypt.Length); decrypt.FlushFinalBlock(); // 触发填充处理,替代原有的Flush+Close return Encoding.UTF8.GetString(decryptionStreamBacking.ToArray()); } } }
关键修正点说明
- 密码编码统一:将C#中的密码字符串转为UTF-8字节数组,确保和Node.js的
pbkdf2Sync密钥派生逻辑完全一致 - CryptoStream正确处理:调用
FlushFinalBlock()确保解密时正确处理PKCS#7填充,避免填充验证错误 - 资源安全管理:使用
using语句自动释放AES、流等资源,避免内存泄漏 - 明确IV变量:直接创建16字节的IV数组,消除原代码中IV变量未定义的潜在风险
验证步骤
- 用Node.js的
Encrypt方法生成测试加密数据 - 用修正后的C#
Decrypt方法解密该数据,验证结果与原始明文一致 - 分别在Node.js和C#中输出密钥的十六进制值,确认派生的密钥完全相同
内容的提问来源于stack exchange,提问作者Austin Lawrence
相关产品推荐
相关产品推荐

