Wrong size and bytes of decrypted data using AES

时间:2018-02-26 17:38:32

标签: c# .net encryption cryptography aes

In our project we use follow methods to encrypt/decrypt important data before storing. Size of incoming bytes is always 32. Please take a look:

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();
            }
        }
    }
}

This code works great in all cases. But on customer environment we've got 47 bytes instead of 32 during decryption. After some investigation I realized that such behavior may occurs when use incorrect passphrase (not same as encrypted but good enogth to decrypt in another byte combination). But customer very sure that password is correct. Might be a situation when environmental configuration (Windows or .Net updates, security config etc.) cause such problem? Thanks for any help.

Upd. Added example code to prove case with incorrect passphrase. Encrypted string contains 32 bytes from 1 to 32 and encrypted with passphrase "p@ssW0rd". If decrypt it using correct passphrase we'll get 32 bytes back, but if use "p4ssW7rd" result will contain 47 bytes.

        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);

Can be any other case (except incorrect passphrase) to get wrong decrypted data?

1 个答案:

答案 0 :(得分:2)

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十六进制)。