AES 加密后 GZip 幻数丢失

GZip Magic Number Missing After AES Encryption

我正在使用 GZip 压缩文件,然后使用 AES 进行加密。

当我单步执行代码时,我看到中间压缩未加密数据中存在幻数 (1f 8b),然后文件被加密。当我去解密文件时,中间解密的压缩数据不包含幻数,解压时GZipStream失败。

这里是主要代码:

    private static void CompressThenEncrypt(string inputFileName, string outputFileName, ICryptoTransform encryptor)
    {
        using (var inputFileStream = new FileStream(inputFileName, FileMode.Open, FileAccess.Read))
        {
            using (var outputFileStream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write))
            using (var cryptoStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write))
            {
                using (var gZipStream = new GZipStream(cryptoStream, CompressionMode.Compress))
                {
                    inputFileStream.CopyTo(gZipStream);
                }
            }
        }
    }

    private static void DecryptThenDecompress(string inputFileName, string outputFileName, ICryptoTransform decryptor)
    {
        using (var inputFileStream = new FileStream(inputFileName, FileMode.Open, FileAccess.Read))
        {
            using (var cryptoStream = new CryptoStream(inputFileStream, decryptor, CryptoStreamMode.Read))
            using (var gZipStream = new GZipStream(cryptoStream, CompressionMode.Decompress))
            using (var outputFileStream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write))
            {
                gZipStream.CopyTo(outputFileStream);
            }
        }
    }

源文件为 19000 字节。压缩后变为 603 字节(带有幻数),然后加密变为 608 字节(由于填充)。解密后它变成 603 字节(没有幻数),我根本无法超越这一点。

调用代码如下:

        using (var aes = new AesCryptoServiceProvider())
        {
            ICryptoTransform encryptor = aes.CreateEncryptor();
            ICryptoTransform decryptor = aes.CreateDecryptor();

            CompressThenEncrypt(OriginalFileName, CompressThenEncryptFileName, encryptor);

            DecryptThenDecompress(CompressThenEncryptFileName, DecryptThenDecompressFileName, decryptor);
        }

编辑:更多信息

Compressed Data (603 bytes): 1F-8B-08-00-00-00-00-00-04-00-ED-CA-B1-0D-00-20-08-00-C1-DE-C4-21-51-64-FF-11-DC-80-84-FE-9A-6F-FE-E2-DC-7C-15-6D-F7-EA-3F-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-D4-44-7D-3F-A6-A4-8A-30-E6-02-00

Encrypted Data (608 bytes): C3-02-64-A9-15-15-52-A9-9F-2A-62-EF-89-B2-9A-E0-72-9A-BA-E2-A2-C8-35-BA-3C-CF-D7-07-8B-DC-D4-63-65-AF-A8-62-22-E5-CC-C6-7D-6F-9B-64-57-9F-C0-94-75-E7-6C-A9-DB-B3-57-29-01-95-F5-B9-E5-3D-FD-AB-EC-E3-AC-AF-EB-A6-C0-81-B3-47-4A-EB-F6-DD-03-DF-92-0A-82-D9-DD-4E-46-DF-55-2E-EB-34-AC-98-1A-7E-A9-25-94-2D-E8-32-4B-F9-2A-61-64-CB-09-9C-D5-8C-A2-0A-C1-22-90-98-93-26-A6-9F-69-F8-EE-6E-95-96-56-28-71-3B-94-1E-5F-50-DB-15-DF-C6-46-3F-04-57-5E-0B-47-44-BB-13-9F-14-08-FB-87-E6-97-65-1B-CA-50-52-7F-10-5D-AF-CC-2F-5E-D0-39-A6-C4-70-3B-90-5F-63-EA-F0-59-46-9E-99-2D-31-34-66-5D-72-E5-85-D5-00-1E-E7-B2-1C-3B-E0-E8-F8-35-BF-90-24-00-DC-47-09-66-92-2F-43-92-48-CB-42-4C-3C-86-CC-67-33-62-A1-1E-76-D1-D6-AD-5F-50-DC-D9-C7-31-F2-33-FE-77-CB-4C-EE-2A-AE-54-63-46-48-B4-FA-6F-0A-E0-1B-F2-F6-C2-D0-E0-24-A1-79-B8-29-FB-04-F3-D5-4E-CC-64-E8-FA-67-55-7B-E6-CD-FD-D2-13-3D-F0-C4-10-A9-5C-BB-34-66-54-A4-6E-B7-AC-54-7A-21-5F-C3-01-7B-97-AD-71-C5-2A-E9-39-B1-27-C7-F8-AC-BF-76-EA-D6-C8-05-22-54-4B-71-73-F2-FD-8E-6E-D7-D2-F2-F7-83-B3-9F-75-3C-CA-BA-BD-F3-C2-E6-16-37-9E-C1-88-C4-69-F5-95-E8-A5-81-C9-FC-22-73-1D-09-AB-A8-6D-A3-BA-CB-0F-27-4E-C7-8C-7A-6C-9D-9B-9D-1F-45-F2-7C-B7-7B-F6-DF-24-50-71-A7-BA-F7-F2-8C-AC-19-A3-86-77-4A-EC-5A-06-12-1F-00-AD-5D-EE-E0-61-D0-80-B7-2C-72-2E-77-6A-32-24-0C-64-78-63-37-A9-08-A2-90-9D-21-5E-E4-5E-E6-1D-66-7D-F5-E1-FC-3C-F1-DE-76-68-D7-1D-46-70-A5-32-31-2B-F5-02-6A-E4-95-CD-8E-B8-76-8D-6E-0B-98-E9-4A-DD-8D-C3-6F-D8-0C-BE-C7-32-3F-99-26-94-26-41-80-E4-E6-E7-D3-26-E9-2F-C0-5D-7B-98-24-BD-6E-9B-E8-9D-F6-DF-51-90-FE-EE-86-DE-9E-31-9F-1C-BA-1A-C4-5B-DD-5A-84-43-02-B9-99-01-6A-95-7C-FF-86-28-C2-4C-EF-4D-D6-36-BD-08-0F-30-25-E7-FF-D9-BC-DA-A7-87-65-1A-1E-83-55-D2-60-38-EC-51-97-FA-FD-11-70-83-70-66-39-78-47-93-7C-B7-FB-48-96-2F-C6-1E-6E-7D-29-38-F6-AB-06-45-C5-F7-50-D6-C7-44-5C-AB-96-A0-60-7D-0E-63-4E-B2-EA

Decrypted Data (603 Bytes): 66-90-D0-0F-8B-67-60-9B-AC-39-FC-45-04-3F-9D-C5-08-00-C1-DE-C4-21-51-64-FF-11-DC-80-84-FE-9A-6F-FE-E2-DC-7C-15-6D-F7-EA-3F-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-14-45-51-D4-44-7D-3F-A6-A4-8A-30-E6-02-00

如您所见,除了前 16 个字节外,所有数据在压缩格式中都是相同的:

加密前:1F-8B-08-00-00-00-00-00-04-00-ED-CA-B1-0D-00-20

解密后:66-90-D0-0F-8B-67-60-9B-AC-39-FC-45-04-3F-9D-C5

神奇的数字不见了,我不知道为什么。

我正在加密的文件是一个文本文件,abcdefabcdefabcdefabcdefabcdefabcdef 行重复了 5000 次。

只有前 16 个字节(AES 的默认块大小)不正确是另一个问题的特征,即 AesCryptoServiceProvider 解密器对象不能被重用,因为它存储了最后一个的状态信息导致后续解密出现这种奇怪结果的解密。

完整的原始调用代码包括对解密器对象的致命重用。

完整的原始调用代码:

using (var aes = new AesCryptoServiceProvider())
{
      ICryptoTransform encryptor = aes.CreateEncryptor();
      ICryptoTransform decryptor = aes.CreateDecryptor(); // <-- Decryptor fails on second usage

      // Compress/ Encrypt
      CompressThenEncrypt(OriginalFileName, CompressThenEncryptFileName, encryptor);
      EncryptThenCompress(OriginalFileName, EncryptThenCompressFileName, encryptor);

      // Decrypt/ Decompress
      DecompressThenDecrypt(EncryptThenCompressFileName, DecompressThenDecryptFileName, decryptor);
      DecryptThenDecompress(CompressThenEncryptFileName, DecryptThenDecompressFileName, decryptor);
}

这是我开发的个人练习,旨在展示压缩 然后 加密数据的重要性。我有两个场景 "Compress Then Encrypt" 和 "Encrypt Then Compress" 并且我还编写了执行反向操作的代码。

问题发生在我重新使用 ICryptoTransform 解密器对象时。尽管 CanReuseTransform 属性 是 true - 这是虚假广告。根据 this answer,似乎存在一个错误,即解密后输入缓冲区未正确清除。链接的答案描述了一些解决方法,只需创建一个单独的解密器对象即可。

修改后的工作调用代码:

using (var aes = new AesCryptoServiceProvider())
{
      ICryptoTransform encryptor = aes.CreateEncryptor();
      ICryptoTransform decryptor = aes.CreateDecryptor();
      ICryptoTransform decryptor2 = aes.CreateDecryptor();

      // Compress/ Encrypt
      CompressThenEncrypt(OriginalFileName, CompressThenEncryptFileName, encryptor);
      EncryptThenCompress(OriginalFileName, EncryptThenCompressFileName, encryptor);

      // Decrypt/ Decompress
      DecompressThenDecrypt(EncryptThenCompressFileName, DecompressThenDecryptFileName, decryptor);
      DecryptThenDecompress(CompressThenEncryptFileName, DecryptThenDecompressFileName, decryptor2);
}