C# RijndaelManaged解密结果末尾带\0致单元测试失败排查
Rijndael加解密结果末尾多
\0空字符问题排查与修复 问题背景
实现了一套数据加解密工具类,向加密方法传入测试字符串"This is a test.",完成加解密流程后得到的结果字符串末尾多出\0空字符,二者语义等价但无法通过字符串相等断言的单元测试。最初怀疑是PaddingMode.PKCS7填充模式导致返回的字节缓冲区末尾携带额外\0空字符,最终通过调整解密方法返回逻辑解决问题:仅截取CryptoStream实际读取到的readCount长度字节作为返回结果,所有代码块、技术术语均保持原有写法未做修改。
对应单元测试代码
var privateKey = Encryption.GetPrivateKey(); var password = new byte[] { 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }; var salt = Salt.CreateSalt(); var passPhrase = Encryption.PasswordAndSalt(password, salt); var thisIsATest = "This is a test."; var clearArray = Encoding.UTF8.GetBytes(thisIsATest); var cypherArray = Encryption.Encrypt(clearArray, passPhrase, privateKey); var decryptedArray = Encryption.Decrypt(cypherArray, passPhrase, privateKey); var thisIsDecrypted = Encoding.UTF8.GetString(decryptedArray); //Assert Assert.IsTrue(thisIsATest == thisIsDecrypted);
原有问题代码
Decrypt解密方法(修复前)
public static byte[] Decrypt(byte[] cypherBytes, byte[] passPhrase, byte[] privateKey) { var result = new byte[] { }; using (var rijndael = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { using (var decryptor = rijndael.CreateDecryptor(passPhrase, privateKey)) { using (var ms = new MemoryStream(cypherBytes)) { using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) { result = new byte[cypherBytes.Length]; var readCount = cs.Read(result, 0, result.Length); } } } rijndael.Clear(); } return result; }
Encrypt加密方法
public static byte[] Encrypt(byte[] clearBytes, byte[] passPhrase, byte[] privateKey) { var result = new byte[] { }; using (var rijndael = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { using (var encryptor = rijndael.CreateEncryptor(passPhrase, privateKey)) { using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.FlushFinalBlock(); } result = ms.ToArray(); } } rijndael.Clear(); } return result; }
PasswordAndSalt密钥派生方法
public const int INITIALIZATION_VECTOR_SIZE = 32; public static byte[] PasswordAndSalt(byte[] password, byte[] salt) { var result = new byte[] { }; using (var pwd = new PasswordDeriveBytes(password, salt)) { result = pwd.GetBytes(INITIALIZATION_VECTOR_SIZE); } return result; }
问题根因
该问题与PaddingMode.PKCS7填充模式无关。
原有解密逻辑预先分配了和密文等长的字节数组作为接收缓冲区,调用CryptoStream.Read方法时,只会将实际解密得到的有效明文写入缓冲区前readCount个位置,数组剩余未被写入的位置会保留字节类型默认值0x00(即对应\0空字符)。代码直接返回整个长度等于密文的缓冲区,会把末尾的无效0x00字节一并返回,通过Encoding.UTF8.GetString转成字符串后,末尾就会携带多余空字符,导致和原字符串的相等断言失败。
修复方案
调整解密方法返回逻辑,不直接返回预分配的全长度缓冲区,而是根据Read方法返回的实际读取字节数readCount,从临时缓冲区中拷贝对应长度的有效字节作为返回结果,丢弃缓冲区末尾未被写入的无效0x00字节即可。
修复后的Decrypt方法代码如下:
public static byte[] Decrypt(byte[] cypherBytes, byte[] passPhrase, byte[] privateKey) { var result = new byte[] { }; using (var rijndael = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { using (var decryptor = rijndael.CreateDecryptor(passPhrase, privateKey)) { using (var ms = new MemoryStream(cypherBytes)) { using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) { var array = new byte[cypherBytes.Length]; var readCount = cs.Read(array, 0, array.Length); result = new byte[readCount]; Array.Copy(array, result, readCount); } } } rijndael.Clear(); } return result; }
修复后解密得到的字节数组和原始明文字节数组完全一致,单元测试可正常通过。
内容的提问来源于stack exchange,提问作者user153923
相关产品推荐
相关产品推荐

