使用特定偏移量的AES解密时输入数据不是完整的块?

The input data is not a complete block when decrypting AES with specific offset?

我正在尝试编写一个 encryption/decryption 方法进行练习,在初步 运行 工作后,我决定通过将 IV 加密到数据中来加强它并使其不易受攻击.我得到了它的工作,并决定通过在 IV 的左侧添加一些随机数据来为 IV 在数据中的位置引入偏移来再次加强它。到目前为止,一切正常,但现在我收到一条解密错误消息:

The input data is not a complete block.

以我对加密和解密的有限了解,这对我调试问题毫无用处。我搜索了高低,none 这个问题的答案,我发现似乎可以解决我的问题。答案通常是开发人员没有解密 byte[] 而是类似于 base 64 字符串的东西。

private static Guid TestGuid = Guid.NewGuid();
private static DateTime Timestamp = DateTime.Now;
private static string key = "PPPQmyuzqKtjzYlWM3mP0aDxaxCzlsACajIkTVN4IjI=";
public static void Main()
{
    string data = TestGuid + "|" + Timestamp;
    Console.WriteLine("Request Parameter: " + data);
    string encryptedData = AESEncrypt(key, data, 1);
    Console.WriteLine("Encrypted: " + encryptedData);
    string decryptedData = AESDecrypt(key, encryptedData, 1);
    Console.WriteLine("Decrypted: " + decryptedData);
}

public static string AESEncrypt(string key, string data, int offset)
{
    if (string.IsNullOrWhiteSpace(data))
        throw new ArgumentException("Data");
    byte[] encryptedData;
    byte[] keyData = Convert.FromBase64String(key);
    using (Aes algo = Aes.Create())
    {
        algo.Key = keyData;
        algo.GenerateIV();
        algo.Padding = PaddingMode.PKCS7;
        byte[] iv = new byte[offset + 16];
        Random r = new Random();
        using (MemoryStream ms = new MemoryStream())
        {
            for (int i = 0; i < offset; i++)
                iv[i] = (byte)r.Next(1, 200);
            for (int i = 0; i < algo.IV.Length; i++)
                iv[offset + i - 1] = algo.IV[i];
            ICryptoTransform encryptor = algo.CreateEncryptor(algo.Key, algo.IV);
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                using (BinaryWriter bw = new BinaryWriter(cs))
                {
                    bw.Write(iv, 0, offset);
                    ms.Write(iv, offset, algo.IV.Length);
                    bw.Write(data);
                    cs.FlushFinalBlock();
                }

                encryptedData = ms.ToArray();
            }
        }
    }

    if (encryptedData != null)
        return Convert.ToBase64String(encryptedData);
    throw new Exception("An unxpected error occurred and the provided data was not encrypted.");
}

public static string AESDecrypt(string key, string data, int offset)
{
    if (string.IsNullOrWhiteSpace(data))
        throw new ArgumentException("Data");
    string decryptedData;
    byte[] keyData = Convert.FromBase64String(key);
    using (Aes algo = Aes.Create())
    {
        algo.Key = keyData;
        algo.Padding = PaddingMode.PKCS7;
        byte[] decodedData = Convert.FromBase64String(data);
        using (MemoryStream ms = new MemoryStream(decodedData))
        {
            byte[] ivData = new byte[offset + 16];
            ms.Read(ivData, 0, offset + 16);
            List<byte> iv = new List<byte>();
            for (int i = offset - 1; i < ivData.Length - 1; i++)
                iv.Add(ivData[i]);
            algo.IV = iv.ToArray();
            ICryptoTransform decryptor = algo.CreateDecryptor(algo.Key, algo.IV);
            List<byte> dataToDecrypt = new List<byte>();
            for (int i = 0; i + offset < decodedData.Length; i++)
                dataToDecrypt.Add(decodedData[i + offset]);
            using (MemoryStream ds = new MemoryStream(dataToDecrypt.ToArray()))
            {
                using (CryptoStream cs = new CryptoStream(ds, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader sr = new StreamReader(cs))
                    {
                        decryptedData = sr.ReadToEnd();
                    }
                }
            }
        }
    }

    if (!string.IsNullOrWhiteSpace(decryptedData))
        return decryptedData;
    throw new Exception("An unxpected error occurred and the provided data was not decrypted.");
}

是什么导致了这个错误,为什么会导致错误,我该如何解决这个错误,为什么这个解决方案有效?

在加密过程中弄乱最终的加密流(将原始字节输入其中)是一个致命错误,应该避免

出现问题是因为

bw.Write(iv, 0, offset);
ms.Write(iv, offset, algo.IV.Length);

将修改后的 IV 的第一个随机字节提供给加密流,并将其其余部分提供给原始输出流,修改后的 iv 的第一个字节不会立即写入内存流,而是一部分第一个加密块,因此,解密期间密码的大小将缺少一些字节,例如它缺少 1 个字节,其中 offset = 1

但是您希望它们立即作为独立字节写入,因为在解密部分您从流中读取了偏移量 + 16 个字节,因此您读入了加密块并使其小于块大小AES。如果调试代码,您可以看到这一点。加密字节的最终大小是 0x50 而解密字节的大小是 0x50 - offset = 0x4f (offset = 1)

对于解决方案,

  1. 您可以从密钥中导出 IV(如果您重复使用密钥,这将是不安全的)并且不必将其包含在您的密码中。
  2. 您可以将 IV(和您的随机字节)添加到您的加密缓冲区并先读取它,然后使用它进行解密,

赞:

public static string AESEncrypt(string key, string data, int offset)
{
    if (string.IsNullOrWhiteSpace(data))
        throw new ArgumentException("Data");
    byte[] encryptedData;
    byte[] keyData = Convert.FromBase64String(key);
    using (Aes algo = Aes.Create())
    {
        algo.Key = keyData;
        algo.GenerateIV();
        algo.Padding = PaddingMode.PKCS7;
        Random r = new Random();
        using (MemoryStream ms = new MemoryStream())
        {
            for (int i = 0; i < offset; i++)
            {
                ms.WriteByte((byte)r.Next(0, 200));
            }
            ms.Write(algo.IV, 0, 16);

            ICryptoTransform encryptor = algo.CreateEncryptor(algo.Key, algo.IV);
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                using (BinaryWriter bw = new BinaryWriter(cs))
                {
                    bw.Write(data);
                    cs.FlushFinalBlock();
                }

                encryptedData = ms.ToArray();
            }
        }
    }

    if (encryptedData != null)
        return Convert.ToBase64String(encryptedData);
    throw new Exception("An unxpected error occurred and the provided data was not encrypted.");
}

public static string AESDecrypt(string key, string data, int offset)
{
    if (string.IsNullOrWhiteSpace(data))
        throw new ArgumentException("Data");
    string decryptedData;
    byte[] keyData = Convert.FromBase64String(key);
    using (Aes algo = Aes.Create())
    {
        algo.Key = keyData;
        algo.Padding = PaddingMode.PKCS7;
        byte[] decodedData = Convert.FromBase64String(data);
        using (MemoryStream ms = new MemoryStream(decodedData))
        {
            for (int i = 0; i < offset; i++) ms.ReadByte();
            byte[] iv = new byte[16];
            ms.Read(iv, 0, 16);

            algo.IV = iv.ToArray();

            ICryptoTransform decryptor = algo.CreateDecryptor(algo.Key, algo.IV);

            using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader sr = new StreamReader(cs))
                {
                    decryptedData = sr.ReadToEnd();
                }
            }
        }
    }

    if (!string.IsNullOrWhiteSpace(decryptedData))
        return decryptedData;
    throw new Exception("An unxpected error occurred and the provided data was not decrypted.");
}