C# TripleDES分块加密解密报‘Bad data’异常排查求助
解密时“Bad data”异常的排查与修复方案
嘿,我帮你揪出了两个核心问题——这就是你分块处理时解密失败的根源,而用TransformFinalBlock能正常运行,是因为它自动帮你处理了这些收尾步骤:
问题1:加密时错误使用GetBuffer()获取数据
MemoryStream.GetBuffer()返回的是流的底层缓冲区,这个缓冲区的长度会大于等于实际写入的有效数据(因为MemoryStream会预分配空间)。你把整个缓冲区转成Base64字符串,相当于给解密端传递了一堆无效的额外字节,TripleDES解密时识别到这些不属于加密数据的内容,直接抛出“Bad data”异常。
正确的做法是用MemoryStream.ToArray(),它只返回实际写入流的有效字节。
问题2:CryptoStream未调用FlushFinalBlock()处理收尾
分块加密/解密时,CryptoStream内部会维护一个缓冲区,最后一批数据可能还留在缓冲区里没写入目标流,尤其是PKCS7 padding的场景下,必须明确调用FlushFinalBlock()来处理最后的padding字节(加密时添加padding,解密时移除padding)。虽然Close()会间接触发这个操作,但手动调用更可靠,能确保所有数据都被正确处理。
修正后的加密代码
public string EncryptData(byte[] fileData, byte[] tdesKey, byte[] tdesIV) { using (MemoryStream msInput = new MemoryStream(fileData)) using (MemoryStream msOutput = new MemoryStream()) { byte[] bin = new byte[100]; long rdlen = 0; long totlen = msInput.Length; int len; using (TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider()) { switch (mw.cmb_encryption_mode.Text) { case "ECB": tdes.Mode = CipherMode.ECB; break; case "CBC": tdes.Mode = CipherMode.CBC; break; case "CFB": tdes.Mode = CipherMode.CFB; break; case "OFB": tdes.Mode = CipherMode.OFB; break; default: break; } tdes.Padding = PaddingMode.PKCS7; tdes.Key = tdesKey; // ECB模式不需要IV,跳过无效赋值 if (tdes.Mode != CipherMode.ECB) { tdes.IV = tdesIV; } using (CryptoStream encStream = new CryptoStream(msOutput, tdes.CreateEncryptor(), CryptoStreamMode.Write)) { mw.encryption_progress_bar.Maximum = totlen; while (rdlen < totlen) { len = msInput.Read(bin, 0, 100); encStream.Write(bin, 0, len); rdlen += len; mw.encryption_progress_bar.Dispatcher.Invoke(() => mw.encryption_progress_bar.Value = rdlen, System.Windows.Threading.DispatcherPriority.Background); } // 关键:手动触发收尾处理,确保padding和缓冲区数据写入完成 encStream.FlushFinalBlock(); } // 仅返回有效加密数据 return Convert.ToBase64String(msOutput.ToArray()); } } }
修正后的解密代码
public byte[] DecryptData(byte[] encryptedData, byte[] tdesKey, Header header, byte[] tdesIV) { using (MemoryStream msInput = new MemoryStream(encryptedData)) using (MemoryStream msOutput = new MemoryStream()) { byte[] bin = new byte[100]; long rdlen = 0; long totlen = msInput.Length; int len; using (TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider()) { switch (header.cipherMode) { case "ECB": tdes.Mode = CipherMode.ECB; break; case "CBC": tdes.Mode = CipherMode.CBC; break; case "CFB": tdes.Mode = CipherMode.CFB; break; case "OFB": tdes.Mode = CipherMode.OFB; break; default: break; } tdes.Padding = PaddingMode.PKCS7; tdes.Key = tdesKey; if (tdes.Mode != CipherMode.ECB) { tdes.IV = tdesIV; } using (CryptoStream decStream = new CryptoStream(msOutput, tdes.CreateDecryptor(), CryptoStreamMode.Write)) { mw.decryption_progress_bar.Maximum = totlen; while (rdlen < totlen) { len = msInput.Read(bin, 0, 100); decStream.Write(bin, 0, len); rdlen += len; mw.decryption_progress_bar.Dispatcher.Invoke(() => mw.decryption_progress_bar.Value = rdlen, System.Windows.Threading.DispatcherPriority.Background); } // 关键:手动处理收尾,确保padding被正确移除 decStream.FlushFinalBlock(); } // 仅返回有效解密数据 return msOutput.ToArray(); } } }
额外优化提示
- 我给代码加上了
using语句,确保所有实现IDisposable的对象(流、加密服务、加密流)都能被正确释放,避免资源泄漏。 TripleDESCryptoServiceProvider已被标记为过时,推荐使用TripleDES.Create()来创建加密实例,不过这是次要问题,先解决当前解密失败的核心需求更优先。
内容的提问来源于stack exchange,提问作者Gutts
相关产品推荐
相关产品推荐

