.NET Framework 4.6x迁移至.NET Core 6后AES解密填充无效问题
问题:.NET Core 6解密.NET Framework加密数据时出现填充无效错误
将加密类库从.NET Framework 4.6x迁移至.NET Core 6后,解密原.NET Framework加密的字符串时抛出错误:Padding is invalid and cannot be removed.。代码逻辑、加密配置(填充方式、密钥、初始化向量等)均未修改,原逻辑在.NET Framework中可正常运行,但在.NET Core中解密失败。目前该代码为遗留代码,暂无替换计划,需解决此问题。
疑问:加密字符串在数据库与类库之间的处理是否因.NET Core CLR与.NET Framework 4.6的CLR差异而受影响?
旧代码(.NET Framework 4.6x)
using Previdence.Common.Security.Cryptography.Interfaces; using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace DatabaseTableExport.Common.Security.Cryptography { public class AESEncryption : IEncryptionProvider { private readonly byte[] _key; private readonly byte[] _iv; public AESEncryption(string key) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); _key = pdb.GetBytes(32); _iv = pdb.GetBytes(16); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public string Encrypt(string value) { if (string.IsNullOrWhiteSpace(value)) { return value; } byte[] clearBytes = Encoding.Unicode.GetBytes(value); using (Aes encryptor = Aes.Create()) { if (encryptor != null) { encryptor.Padding = PaddingMode.PKCS7; using (MemoryStream ms = new MemoryStream()) using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(_key, _iv), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.FlushFinalBlock(); value = Convert.ToBase64String(ms.ToArray()); } } } return value; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public string Decrypt(string value) { if (string.IsNullOrWhiteSpace(value)) { return value; } byte[] cipherBytes = Convert.FromBase64String(value); using (Aes encryptor = Aes.Create()) { if (encryptor != null) { encryptor.Padding = PaddingMode.PKCS7; using (MemoryStream ms = new MemoryStream()) using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(_key, _iv), CryptoStreamMode.Write)) { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.FlushFinalBlock(); value = Encoding.Unicode.GetString(ms.ToArray()); } } } return value; } public void Dispose() { } } } using System; namespace Security_Legacy.Cryptography.Interfaces { public interface IEncryptionProvider : IDisposable { string Decrypt(string cypheredText); string Encrypt(string plainText); } }
新代码(.NET Core 6)
using DatabaseTableExport.Services.Cryptography.Interfaces; using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace DatabaseTableExport.Services.Cryptography { public class AesEncryption : IEncryptionProvider { private readonly byte[] _key; private readonly byte[] _iv; public AesEncryption(string key) { Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); _key = pdb.GetBytes(32); _iv = pdb.GetBytes(16); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public string Encrypt(string value) { if (string.IsNullOrWhiteSpace(value)) { return value; } byte[] clearBytes = Encoding.Unicode.GetBytes(value); using (Aes encryptor = Aes.Create()) { if (encryptor != null) { encryptor.Padding = PaddingMode.PKCS7; using (MemoryStream ms = new MemoryStream()) using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(_key, _iv), CryptoStreamMode.Write)) { cs.Write(clearBytes, 0, clearBytes.Length); cs.FlushFinalBlock(); value = Convert.ToBase64String(ms.ToArray()); } } } return value; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public string Decrypt(string value) { if (string.IsNullOrWhiteSpace(value)) { return value; } byte[] cipherBytes = Convert.FromBase64String(value); using (Aes encryptor = Aes.Create()) { if (encryptor != null) { encryptor.Padding = PaddingMode.PKCS7; using (MemoryStream ms = new MemoryStream()) using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(_key, _iv), CryptoStreamMode.Write)) { try { cs.Write(cipherBytes, 0, cipherBytes.Length); cs.FlushFinalBlock(); value = Encoding.Unicode.GetString(ms.ToArray()); } catch (Exception e) { value = $"Failed to decrypt: {e.Message}"; var errorString = $"{e.Message}"; } } } } return value; } public void Dispose() { // nothing to dispose throw new NotImplementedException(); } } } using System; namespace Security_Legacy.Cryptography.Interfaces { public interface IEncryptionProvider : IDisposable { string Decrypt(string cypheredText); string Encrypt(string plainText); } }
解决方案
问题根源在于Rfc2898DeriveBytes的默认迭代次数差异:
- .NET Framework 4.6x中,
Rfc2898DeriveBytes默认迭代次数为1000 - .NET Core 2.0及以后版本中,默认迭代次数改为10000
由于代码未显式指定迭代次数,导致.NET Core环境生成的密钥(_key)和初始化向量(_iv)与.NET Framework环境不一致,最终解密时出现填充无效错误。
修改方法
在.NET Core版本的AesEncryption构造函数中,显式指定迭代次数为1000,与.NET Framework保持一致:
public AesEncryption(string key) { // 显式设置迭代次数为1000,匹配.NET Framework默认值 Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes( key, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }, 1000); _key = pdb.GetBytes(32); _iv = pdb.GetBytes(16); }
验证
修改后,.NET Core环境可以正常解密原.NET Framework加密的数据;同时,.NET Core加密的数据也能在.NET Framework环境中正常解密,完全兼容原有逻辑。
内容的提问来源于stack exchange,提问作者Tyson Gibby
相关产品推荐
相关产品推荐

