首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >尝试了解在此AES方法中填充无效的原因

尝试了解在此AES方法中填充无效的原因
EN

Stack Overflow用户
提问于 2021-10-22 15:29:26
回答 1查看 215关注 0票数 0

我正在将一个较旧的应用程序移植到.Net 6,并且遇到了加密/解密方法正在失败的绊脚石。它在.Net 4.x.x下仍然工作得很好。

抛出的错误是,

"Padding is invalid and cannot be removed."

代码:-更新为实际的原始代码。此代码在以.Net 4.7.2为目标时运行良好,但是在将代码移动到.Net 6.0 RC2后,它开始丢失任何超过32个字符的解密字符串,这会导致其他地方出现错误,因为字符串不完整。

获取上下文信息。这是在网络主机和桌面客户端上运行的,用于加密传输中的消息。已更新并验证.Net主机是否发送了正确的加密值(使用.Net 4客户端解密邮件成功)。但是,.Net 6桌面客户端无法正确解密它,并且丢失了解密字符串中的字符。

代码语言:javascript
运行
复制
#region Encrypt method(s)

    private const int Keysize = 256;
    private const int Blocksize = 128;
    private const int DerivationIterations = 1000;

    public async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
    {
        string encrypted = null;
        
        bool valid = false;

        while (!valid)
        {
            encrypted = await Encrypt(plainText, passPhrase);
            if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
            {
                valid = true;
            }
        }
        return encrypted;
    }

    private async Task<string> Encrypt(string plainText, string passPhrase)
    {
        var saltStringBytes = GenerateRandomEntropy(32); // 256 bits
        var ivStringBytes = GenerateRandomEntropy(16); // 128 bits
        byte[] plainTextBytes = Convert.FromBase64String(plainText);
        using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
        {
            var keyBytes = password.GetBytes(Keysize / 8);
            using (var symmetricKey = new AesManaged())
            {
                symmetricKey.KeySize = Keysize;
                symmetricKey.BlockSize = Blocksize;
                symmetricKey.Mode = CipherMode.CBC;
                symmetricKey.Padding = PaddingMode.PKCS7;
                using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                {
                    using (var memoryStream = new MemoryStream())
                    {
                        using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            var cipherTextBytes = saltStringBytes;
                            cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                            cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                            memoryStream.Close();
                            cryptoStream.Close();
                            var encrypted64String = Convert.ToBase64String(cipherTextBytes);
                            return encrypted64String;
                        }
                    }
                }
            }
        }
    }

    private static byte[] GenerateRandomEntropy(int byteSize)
    {
        var randomBytes = new byte[byteSize];
        using (var rngCsp = new RNGCryptoServiceProvider())
        {
            rngCsp.GetBytes(randomBytes);
        }
        return randomBytes;
    }

    #endregion

    #region Decrypt method


    public static async Task<string> Decrypt(string cipherText, string passPhrase)
    {
        try
        {
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Blocksize / 8).ToArray();
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) + Blocksize / 8).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) + Blocksize / 8)).ToArray();
            using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new AesManaged())
                {
                    symmetricKey.KeySize = 256;
                    symmetricKey.BlockSize = Blocksize;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                var plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return null;
    }

    #endregion

这用来调用,

代码语言:javascript
运行
复制
encryptedString = await new EncryptDecrypt().EncryptStringWithValidatedPadding(b64String, Convert.ToBase64String(Encoding.UTF8.GetBytes(passPhrase)));

我假设拯救静脉输液应该可以解决这个问题,但我想知道这里是否有任何明显的缺陷,我只是没有看到。

有人能解释一下吗?

更新:按照建议,我已经将代码重构为以下代码。为了确保底层算法的正常工作,我还将其剥离了一分钟。

参考:https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-6.0

代码语言:javascript
运行
复制
namespace Encryption_Helper
{
    public class EncryptDecrypt
    {
        #region Encrypt method(s)

        private static byte[] bytes = new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };

        private const int Keysize = 256;
        private const int Blocksize = 128;
        private const int DerivationIterations = 1000;

        public static async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
        {
            string encrypted = null;

            bool valid = false;

            while (!valid)
            {
                encrypted = await Encrypt(plainText, passPhrase);
                if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
                {
                    valid = true;
                }
            }
            return encrypted;
        }

        private static async Task<string> Encrypt(string plainText, string passPhrase)
        {
            using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), bytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                var ivBytes = password.GetBytes(Blocksize / 8);
                using (var aes = Aes.Create())
                {
                    aes.Key = keyBytes;
                    aes.IV = ivBytes;

                    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                    using (MemoryStream msEncrypt = new MemoryStream())
                    {
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                            {
                                swEncrypt.Write(plainText);
                            }
                            plainText = Convert.ToBase64String(msEncrypt.ToArray());
                        }
                    }
                }
                return plainText;
            }
        }

        #endregion

        #region Decrypt method


        public static async Task<string> Decrypt(string cipherText, string passPhrase)
        {
            try
            {
                using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), bytes, DerivationIterations))
                {
                    var keyBytes = password.GetBytes(Keysize / 8);
                    var ivBytes = password.GetBytes(Blocksize / 8);
                    using (var aes = Aes.Create())
                    {
                        aes.Key = keyBytes;
                        aes.IV = ivBytes;
                     
                        ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

                        using (var memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                using (StreamReader srDecrypt = new StreamReader(cryptoStream))
                                { 
                                    cipherText = srDecrypt.ReadToEnd();
                                }
                            }
                        }
                        return cipherText;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        #endregion
    }
}

它仍然抛出填充错误!

EN

回答 1

Stack Overflow用户

发布于 2021-10-22 19:36:33

解决了!

代码语言:javascript
运行
复制
public class EncryptDecrypt
    {
        #region Encrypt method(s)

        private const int Keysize = 256;
        private const int Blocksize = 128;
        private const int DerivationIterations = 1000;

        public static async Task<string> EncryptStringWithValidatedPadding(string plainText, string passPhrase)
        {
            string encrypted = null;

            bool valid = false;

            while (!valid)
            {
                encrypted = await Encrypt(plainText, passPhrase);
                if (!string.IsNullOrEmpty(await Decrypt(encrypted, passPhrase)))
                {
                    valid = true;
                }
            }
            return encrypted;
        }

        private static async Task<string> Encrypt(string plainText, string passPhrase)
        {
            var saltStringBytes = GenerateRandomEntropy(Keysize / 8); // 256 bits
            var ivStringBytes = GenerateRandomEntropy(Blocksize / 8); // 128 bits
            byte[] plainTextBytes = Convert.FromBase64String(plainText);
            using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var aes = Aes.Create())
                {
                    aes.KeySize = Keysize;
                    aes.BlockSize = Blocksize;
                    aes.Mode = CipherMode.CBC;
                    aes.Padding = PaddingMode.PKCS7;

                    using (var memoryStream = new MemoryStream())
                    {
                        using (var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(keyBytes, ivStringBytes), CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            var cipherTextBytes = saltStringBytes;
                            cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                            cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                            memoryStream.Close();
                            cryptoStream.Close();
                            var encrypted64String = Convert.ToBase64String(cipherTextBytes);
                            return encrypted64String;
                        }
                    }
                }
            }
        }

        private static byte[] GenerateRandomEntropy(int byteSize)
        {
            var randomBytes = new byte[byteSize];
            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                rngCsp.GetBytes(randomBytes);
            }
            return randomBytes;
        }

        #endregion

        #region Decrypt method


        public static async Task<string> Decrypt(string cipherText, string passPhrase)
        {
            try
            {
                var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
                var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
                var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Blocksize / 8).ToArray();
                var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) + Blocksize / 8).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) + Blocksize / 8)).ToArray();
                using (var password = new Rfc2898DeriveBytes(Convert.FromBase64String(passPhrase), saltStringBytes, DerivationIterations))
                {
                    var keyBytes = password.GetBytes(Keysize / 8);
                    using (var aes = Aes.Create())
                    {
                        aes.KeySize = Keysize;
                        aes.BlockSize = Blocksize;
                        aes.Mode = CipherMode.CBC;
                        aes.Padding = PaddingMode.PKCS7;

                        using (var ms = new MemoryStream(cipherTextBytes))
                        {
                            using (var cs = new CryptoStream(ms, aes.CreateDecryptor(keyBytes, ivStringBytes), CryptoStreamMode.Read))
                            {
                                using (StreamReader srDecrypt = new StreamReader(cs))
                                {

                                    // Read the decrypted bytes from the decrypting stream
                                    // and place them in a string.
                                    cipherText = srDecrypt.ReadToEnd();
                                }
                            }
                        }
                        return cipherText;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        #endregion

更新解密方法后,世界上一切又好起来了。

在我看来,.Net 6打破了嵌套的using循环,在完全设置返回值之前关闭了流。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69679484

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档