【问题标题】:The input data is not a complete block when decrypting AES with specific offset?解密具有特定偏移量的 AES 时,输入数据不是一个完整的块?
【发布时间】:2020-04-30 22:51:59
【问题描述】:

我正在尝试编写一种加密/解密方法来进行练习,在初步运行正常后,我决定加强它并通过将 IV 加密到数据中来降低它的脆弱性。我得到了这个工作,并决定通过在数据中为 IV 的位置引入一个偏移量,通过在 IV 的左侧添加一些随机数据来再次加强它。到目前为止,一切正常,但现在我收到一个解密错误:

输入的数据不是一个完整的块。

由于我对加密和解密的了解有限,这对我调试问题毫无用处。我搜索了高低,但我发现这个问题的答案似乎都没有解决我的问题。答案通常是这样的:开发人员不是在解密 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.");
}

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

【问题讨论】:

  • 在 CryptoStream 中间调用 ms.Write 不是一个好主意,结果可能是随机的。 aes 使用一定大小的字节块。所以解密器还假设输入数据至少与块大小对齐。
  • 你可以在代码审查codereview.stackexchange.com/questions/78434/c-aes-encryption找到一些aes示例
  • IV 在 CBC 中并不是要保密的,因此“加密”它不能提高安全性,也不能隐藏它,或者隐藏它,或者你能想到的任何其他东西。既然你需要IV来解密,如果你已经加密了,我怎么能得到它?

标签: c# encryption aes


【解决方案1】:

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

出现问题是因为

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.");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    相关资源
    最近更新 更多