AES解密偶发“Padding is invalid and cannot be removed”错误排查求助
问题现象
网站A执行加密、网站B执行解密,多数场景正常,但偶尔抛出错误:System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed,错误触发在解密代码的cStream.Close()行,两端均配置了PKCS7填充模式。
原加密解密代码
Public Shared Function Encrypt(ByVal inText As String) As String Dim bytesBuff As Byte() = Encoding.Unicode.GetBytes(inText) Dim mKey As String = System.Configuration.ConfigurationManager.AppSettings("EncodeDecodeKey") Using aes__1 As Aes = Aes.Create() Dim crypto As New Rfc2898DeriveBytes(mKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76}) aes__1.Key = crypto.GetBytes(32) aes__1.IV = crypto.GetBytes(16) aes__1.Padding = PaddingMode.PKCS7 Using mStream As New MemoryStream() Using cStream As New CryptoStream(mStream, aes__1.CreateEncryptor(), CryptoStreamMode.Write) cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.Close() End Using inText = Convert.ToBase64String(mStream.ToArray()) End Using End Using 'encode for use with query string Return HttpUtility.UrlEncode(inText) End Function Public Shared Function Decrypt(ByVal cryptTxt As String) As String Dim mKey As String = System.Configuration.ConfigurationManager.AppSettings("EncodeDecodeKey") cryptTxt = cryptTxt.Replace(" ", "+") cryptTxt = cryptTxt.Replace("-", "+") cryptTxt = cryptTxt.Replace("_", "/") cryptTxt = HttpUtility.HtmlDecode(cryptTxt) Dim bytesBuff As Byte() = Convert.FromBase64String(cryptTxt) Using aes__1 As Aes = Aes.Create() Dim crypto As New Rfc2898DeriveBytes(mKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76}) aes__1.Key = crypto.GetBytes(32) aes__1.IV = crypto.GetBytes(16) aes__1.Padding = PaddingMode.PKCS7 Using mStream As New MemoryStream() Using cStream As New CryptoStream(mStream, aes__1.CreateDecryptor(), CryptoStreamMode.Write) cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.Close() End Using cryptTxt = Encoding.Unicode.GetString(mStream.ToArray()) End Using End Using Return cryptTxt End Function
核心问题分析与修复方案
1. URL编码/解码逻辑错误
加密后用HttpUtility.UrlEncode处理字符串,但解密时先做字符替换再调用HtmlDecode,这会导致UrlEncode后的%2B(对应Base64的+)无法被还原,进而引发Base64解码错误,最终导致填充验证失败。
修复:先调用HttpUtility.UrlDecode还原Url编码的字符串,再处理可能的Base64兼容替换:
' 解密函数中替换原字符串处理逻辑 cryptTxt = HttpUtility.UrlDecode(cryptTxt) cryptTxt = cryptTxt.Replace(" ", "+").Replace("-", "+").Replace("_", "/")
2. CryptoStream未显式处理最终块
虽然Close()会隐式调用FlushFinalBlock(),但显式调用能确保所有加密/解密数据(包括PKCS7填充数据)被完整处理,避免异常场景下的数据截断。
修复:在加密和解密的cStream.Close()前添加cStream.FlushFinalBlock():
' 加密函数中 cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.FlushFinalBlock() cStream.Close() ' 解密函数中 cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.FlushFinalBlock() cStream.Close()
3. 密钥一致性验证
确认网站A和网站B的EncodeDecodeKey配置完全一致(包括大小写、特殊字符),Rfc2898DeriveBytes会基于密钥和盐生成Key/IV,密钥不一致会直接导致解密时填充验证失败。
4. 传输过程中的字符串完整性校验
偶发错误可能源于传输时的字符串截断或篡改(如URL参数过长被截断、中间代理修改),可在加密后对生成的字符串添加哈希校验(如SHA256),解密前先校验哈希确保数据未被修改。
修正后的完整代码示例
加密函数
Public Shared Function Encrypt(ByVal inText As String) As String Dim bytesBuff As Byte() = Encoding.Unicode.GetBytes(inText) Dim mKey As String = System.Configuration.ConfigurationManager.AppSettings("EncodeDecodeKey") Using aes__1 As Aes = Aes.Create() Dim crypto As New Rfc2898DeriveBytes(mKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76}) aes__1.Key = crypto.GetBytes(32) aes__1.IV = crypto.GetBytes(16) aes__1.Padding = PaddingMode.PKCS7 Using mStream As New MemoryStream() Using cStream As New CryptoStream(mStream, aes__1.CreateEncryptor(), CryptoStreamMode.Write) cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.FlushFinalBlock() cStream.Close() End Using inText = Convert.ToBase64String(mStream.ToArray()) End Using End Using Return HttpUtility.UrlEncode(inText) End Function
解密函数
Public Shared Function Decrypt(ByVal cryptTxt As String) As String Dim mKey As String = System.Configuration.ConfigurationManager.AppSettings("EncodeDecodeKey") cryptTxt = HttpUtility.UrlDecode(cryptTxt) cryptTxt = cryptTxt.Replace(" ", "+").Replace("-", "+").Replace("_", "/") Dim bytesBuff As Byte() = Convert.FromBase64String(cryptTxt) Using aes__1 As Aes = Aes.Create() Dim crypto As New Rfc2898DeriveBytes(mKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, &H76}) aes__1.Key = crypto.GetBytes(32) aes__1.IV = crypto.GetBytes(16) aes__1.Padding = PaddingMode.PKCS7 Using mStream As New MemoryStream() Using cStream As New CryptoStream(mStream, aes__1.CreateDecryptor(), CryptoStreamMode.Write) cStream.Write(bytesBuff, 0, bytesBuff.Length) cStream.FlushFinalBlock() cStream.Close() End Using cryptTxt = Encoding.Unicode.GetString(mStream.ToArray()) End Using End Using Return cryptTxt End Function
内容的提问来源于stack exchange,提问作者jvcoach23

