【问题标题】:Error when decrypt word, powerpoint file解密word、powerpoint文件时出错
【发布时间】:2016-10-25 12:44:06
【问题描述】:

我正在使用 aes 密码术来加密文件。

private static void Encrypt(string inputFilePath, string outputfilePath)
    {
        string EncryptionKey = "MAKV2SPBNI99212";
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
            {
                using (CryptoStream cs = new CryptoStream(fsOutput, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open))
                    {
                        //int data;
                        //while ((data = fsInput.ReadByte()) != -1)
                        //{
                        //    cs.WriteByte((byte)data);
                        //}

                        byte[] bytes = new byte[fsInput.Length];
                        while (fsInput.Read(bytes, 0, (int)fsInput.Length) > 0) ;
                        cs.Write(bytes, 0, bytes.Length);
                    }
                }
            }
        }
    }

    private static void Decrypt(string inputFilePath, string outputfilePath)
    {
        string EncryptionKey = "MAKV2SPBNI99212";
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open))
            {
                using (CryptoStream cs = new CryptoStream(fsInput, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
                    {
                        //int data;
                        //while ((data = cs.ReadByte()) != -1)
                        //{
                        //    fsOutput.WriteByte((byte)data);
                        //}
                        byte[] bytes = new byte[fsInput.Length];
                        while (cs.Read(bytes, 0, (int)fsInput.Length) > 0) ;
                        fsOutput.Write(bytes, 0, bytes.Length);
                    }
                }
            }
        }
    }

在主函数中我加密、解密word文件:

Encrypt(@"E:\test.docx", @"E:\test.enc");
Decrypt(@"E:\test.enc", @"E:\test_new.docx");

当我使用 ReadByte 函数加密、解密每个字节时。文件 test_new.docx 已创建并正常打开。但是当我使用读取函数加密、解密许多字节时,文件 test_new.docx 被创建但无法打开,错误内容。 有人有想法吗?谢谢!

【问题讨论】:

    标签: c# .net encryption cryptography


    【解决方案1】:

    您使用错误的流长度进行解密,而不是 fsInput.Length 您应该使用 cs.Length 因为这是您实际读取的流。 FileStream 和 CryptoStream 的长度不同,因此您不能互换它们。但是,CryptoStream 也是非搜索的,这意味着它无法搜索流的末尾,并且使用 .Length 将引发 NotImplementedException。因此,对于像 CryptoStream 这样的非搜索流,您必须使用 ReadByte() 一次读取一个字节,直到流说它已完成。

    【讨论】:

    • new BinaryReader(cs).ReadBytes((int)fsInput.Length) 将一次读取正确数量的字节。对于 Office 文档的典型文件大小,这应该比一次读取一个字节具有更好的性能。
    • 我使用了cs.Length,但是它抛出异常,cs.Length 无法访问。
    • 确实如此。当我比较加密后和解密前的文件流长度时,这是不同的。我想在加密、解密大文件时读取很多字节以提高速度。这个bug只在ecrypt、解密word、powerpoint文件、其他文件正常工作时才会出现。
    【解决方案2】:

    我在加密文件之前添加了一些字节。解密字节数组后,我发现这个字节可以准确地得到文件长度。

    【讨论】:

    • 如果您只使用我回答中的 3 行代码,您绝对不必添加这些额外的字节。我测试了我的代码,它无需任何其他修改即可工作。 (我不知道为什么它曾经被否决。)
    【解决方案3】:

    问题是解密后的bytes数组末尾的零太多了。这是因为使用的密码要求每个加密块都匹配块大小,如果不合适,它就会被填满。所以fsInput.Length实际上大于解密字节的长度。

    要消除解密字节中的这些零,请在您的 Decrypt 方法中替换此代码

    using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
    {
        byte[] bytes = new byte[fsInput.Length];
        while (cs.Read(bytes, 0, (int)fsInput.Length) > 0) ;
        fsOutput.Write(bytes, 0, bytes.Length);
    }
    

    使用此代码

    using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
    using (BinaryWriter fsDecrypted = new BinaryWriter(fsOutput))
    using (BinaryReader br = new BinaryReader(cs))
        fsDecrypted.Write(br.ReadBytes((int)fsInput.Length));
    

    现在它应该可以按预期工作了。

    【讨论】:

    • 默认填充是 PKCS#7 PaddingMode.PKCS7 不是空填充。 MSDN 上的SymmetricAlgorithm Properties 页面。请参阅@Neil Humby 的回答。
    • @zaph 你完全正确,我更正了我的答案。但我认为使用 BinaryReader 一次读取所有内容在性能方面仍然比使用 ReadByte() 像 Neil Humby 建议的那样好。
    • @zaph 解密后的bytes 中的零不是来自 PKCS#7 填充,而是来自byte[] bytes = new byte[fsInput.Length];,因为fsInput.Length 实际上太大了。我更正了我的答案以反映这方面。我的回答还有错误吗? (我愿意学习。)
    • 注意fsInput.Length在一般情况下是不知道的,加密和解密在距离和时间上是分开的。如果您为加密和解密指定相同的填充选项并在最后一个被解密的字节上调用 final,则解密的数据将与加密的纯数据匹配。不需要摆弄。如果创建的缓冲区大于解密数据,则将缓冲区截断为解密数据的报告大小。
    • @haindl 我已经测试了你的代码。这是完美的工作。 BinaryReader 的 ReadBytes 函数准确返回字节长度。
    猜你喜欢
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-31
    • 2021-05-13
    • 1970-01-01
    相关资源
    最近更新 更多