使用CryptoStream链接的ZipInputStream(SharpZipLib)将导致GetNextEntry()在最后一次条目

时间:2017-10-16 09:15:11

标签: c# encryption cryptography zip

我注意到一种我无法控制的奇怪行为。 Zip文件正在加密如下:

private static void EncryptFile(string source, string destination, string password, int iteration)
    {
        var keyGenerator = new Rfc2898DeriveBytes(password, 1000, iteration);

        using (var aes = new RijndaelManaged
        {
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7,
            KeySize = 128,
            BlockSize = 128
        })
        {
            aes.Key = keyGenerator.GetBytes(16);
            aes.IV = keyGenerator.GetBytes(16);

            using (var sourceStream = File.OpenRead(source))
            {
                using (var destinationStream = File.OpenWrite(destination))
                {
                    using (var cs = new CryptoStream(destinationStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        //salt (uncrypted)
                        destinationStream.Write(keyGenerator.Salt, 0, keyGenerator.Salt.Length);
                        //rest (crypted)
                        sourceStream.CopyTo(cs);
                    }
                }
            }
        }
    }

此方法将动态解密和解压缩文件。

private static void UnzipDecryptFile(string source, string destination, string password, int iteration)
    {
        using (var aes = new RijndaelManaged
                     {
                         Mode = CipherMode.CBC,
                         Padding = PaddingMode.PKCS7,
                         KeySize = 128,
                         BlockSize = 128
                     })
        {
            using (var sourceStream = File.OpenRead(source))
            {
                var salt = new byte[1000];
                sourceStream.Read(salt, 0, 1000);

                var keyGenerator = new Rfc2898DeriveBytes(password, salt, iteration);

                aes.Key = keyGenerator.GetBytes(16);
                aes.IV = keyGenerator.GetBytes(16);

                using (var cs = new CryptoStream(sourceStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    using (var zipStream = new ZipInputStream(cs))
                    {
                        ZipEntry entry;

                        while ((entry = zipStream.GetNextEntry()) != null)
                        {
                            var c = entry.DateTime;
                            //do stuff with entry
                        }
                    }
                }
            }
        }
    }

不幸的是我无法使用System.IO.Compression.ZipArchive(Stream stream),因为如果流不支持 CanSeek ,它会将整个流复制到内存,哪个CryptoStream没有。

在最后一次调用zipStream.GetNextEntry()时,我收到一个CryptographicException:填充无效,无法删除。

   at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream.Close()
   at ICSharpCode.SharpZipLib.Zip.ZipInputStream.Close()
   at ICSharpCode.SharpZipLib.Zip.ZipInputStream.GetNextEntry()

GetNextEntry() will cause close on stream

这似乎是正确的行为。 ZipInputstream.GetNextEntry()没有找到下一个条目,因此它会关闭所有内容。

由于某些原因,CryptoStream.Dispose()方法认为它应该调用 FlushFinalBlock()但是因为所有内容都已经解密,所以它会失败。

如果我使用 PaddingMode.Zeros 而不是 PadddingMode.PKCS7 它会起作用,但这不是一个选项,因为这种模式在java中不可用(默认情况下将生成zip文件的那一方)。

我错过了什么?

[编辑] 对我来说更精确:解密/解包工作正常。在最后一次调用 zipStream.GetNextEntry()之后处理期间发生错误。对于一些Zip文件,根本不会发生错误。这可能是大小为16(块大小)的文件,不需要填充。

0 个答案:

没有答案