使用AES的解密数据的大小和字节错误

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用AES的解密数据的大小和字节错误相关的知识,希望对你有一定的参考价值。

在我们的项目中,我们使用follow方法在存储之前加密/解密重要数据。传入字节的大小始终为32.请查看:

public static string Encrypt(byte[] data, string pass)
{
    using (var algorithm = new RijndaelManaged())
    {
        algorithm.Padding = PaddingMode.PKCS7;

        var salt = new byte[32];
        new Random().NextBytes(salt);

        using (var rng = new Rfc2898DeriveBytes(pass, salt, 3072))
        {
            algorithm.Key = rng.GetBytes(algorithm.KeySize / 8);
            algorithm.IV = rng.GetBytes(algorithm.BlockSize / 8);

            using (var oms = new MemoryStream())
            {
                using (var ims = new MemoryStream(data))
                {
                    var encryptor = algorithm.CreateEncryptor();
                    var cs = new CryptoStream(oms, encryptor, CryptoStreamMode.Write);
                    ims.CopyTo(cs);
                    cs.FlushFinalBlock();
                }

                oms.Flush();

                var target = new byte[oms.Length + salt.Length];
                oms.ToArray().CopyTo(target, 0);
                salt.CopyTo(target, oms.Length);

                return Convert.ToBase64String(target);
            }
        }
    }
}

public static byte[] Decrypt(string data, string pass)
{
    var allbytes = Convert.FromBase64String(data);

    var salt = new byte[32];
    var databytes = new byte[allbytes.Length - salt.Length];

    Array.Copy(allbytes, databytes.Length, salt, 0, salt.Length);
    Array.Copy(allbytes, 0, databytes, 0, databytes.Length);

    using (var algorithm = new RijndaelManaged())
    {
        algorithm.Padding = PaddingMode.PKCS7;

        using (var rng = new Rfc2898DeriveBytes(pass, salt, 3072))
        {
            algorithm.Key = rng.GetBytes(algorithm.KeySize / 8);
            algorithm.IV = rng.GetBytes(algorithm.BlockSize / 8);

            using (var oms = new MemoryStream())
            {
                using (var ims = new MemoryStream(databytes))
                {
                    var decryptor = algorithm.CreateDecryptor();
                    using (var cs = new CryptoStream(ims, decryptor, CryptoStreamMode.Read))
                    {
                        cs.CopyTo(oms);
                    }
                }

                return oms.ToArray();
            }
        }
    }
}

此代码适用于所有情况。但在客户环境中,我们在解密期间有47个字节而不是32个字节。经过一些调查后,我意识到当使用不正确的密码短语时可能会发生这种行为(与加密相同,但在另一个字节组合中解密时很好)。但客户非常确定密码是否正确。当环境配置(Windows或.Net更新,安全配置等)导致此类问题时,可能会出现这种情况吗?谢谢你的帮助。

UPD。添加了示例代码以证明具有错误密码的案例。加密字符串包含从1到32的32个字节,并使用密码“p @ ssW0rd”进行加密。如果使用正确的密码解密它,我们将获得32个字节,但如果使用“p4ssW7rd”结果将包含47个字节。

        var password = "p@ssW0rd";
        var incorrectPassword = "p4ssW7rd";
        var encryptedData = "gP/MV6S09UYWc0pMgkkIqEdg204rToV/FQLpvktArWjAlIqjpbiPg5YX9zhPA9/gRuSbNtU5nyBKst54041uGeDNKSYJYvJc1UKZrMcqVFw=";

        var decryptedData = Decrypt(encryptedData, password);
        var incorrectDecryptedData = Decrypt(encryptedData, incorrectPassword);
        Console.WriteLine("Decrypted size: {0}, incorrect size: {1}", decryptedData.Length, incorrectDecryptedData.Length);

可以是任何其他情况(不正确的密码短语)来获取错误的解密数据?

答案

AES-CBC很乐意解密为无效甚至随机数据。哪些数据取决于(错误)密钥和密文。此外,PKCS#7 unpadding不执行任何完整的错误检测。它的创建只是为了确保明文由16字节块组成,因为AES一次只能加密16个字节(并且CBC操作模式通常不会改变它)。

所以会发生的情况是,对于某些密码,您可能会得到一个产生随机密文的密钥,这会偶然产生有效的PKCS#7填充。最可能的填充当然是具有值01的单个字节,其仅表示填充的一个字节,可能性为1/256。然后有两个字节填充0202,但只有1/65536的可能性等。

要解决这种情况,您应该计算IV和密文的HMAC,或使用经过验证的操作模式,如GCM。这将捕获100%错误的密钥/密文组合。


所以47字节的明文是3 * 16 - 1,所以你生成了一个1字节的填充 - 最简单的填充 - 偶然。由于填充始终发生,32字节的明文应该确实扩展到48字节的密文,最多16个字节的填充(全部值为10,十六进制)。

以上是关于使用AES的解密数据的大小和字节错误的主要内容,如果未能解决你的问题,请参考以下文章

AES GCM 解密绕过 JAVA 中的身份验证

混淆的 AES 解密程序集

Java解密错误:数据未对齐块大小

C# Aes byte[] 加密/解密:不是一个完整的块

关于蓝牙通信的数据AES128 ECB加密解密

针对C#和javascript的兼容AES加密和解密