C# 解密word、powerpoint文件时出错

C# 解密word、powerpoint文件时出错,c#,.net,encryption,cryptography,C#,.net,Encryption,Cryptography,我正在使用aes加密来加密文件 private static void Encrypt(string inputFilePath, string outputfilePath) { string EncryptionKey = "MAKV2SPBNI99212"; using (Aes encryptor = Aes.Create()) { Rfc2898DeriveBytes pdb = new Rfc2898De

我正在使用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);
                    }
                }
            }
        }
    }
在主函数i中,加密、解密word文件:

Encrypt(@"E:\test.docx", @"E:\test.enc");
Decrypt(@"E:\test.enc", @"E:\test_new.docx");
当我使用ReadByte函数加密时,解密每个字节。文件test_new.docx已创建并正常打开。但当我使用Read函数加密、解密许多字节时,文件test_new.docx被创建但无法打开,错误内容。
你有什么想法吗?谢谢

问题在于解密的
字节
数组末尾的零太多。这是因为使用的密码要求每个加密块与块大小匹配,如果不匹配,则会被填满。所以
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));

现在它应该可以正常工作了。

问题是解密的
字节
数组末尾有太多的零。这是因为使用的密码要求每个加密块与块大小匹配,如果不匹配,则会被填满。所以
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));

现在,它应该可以正常工作。

您使用了错误的流长度进行解密,而不是fsInput.length,您应该使用cs.length,因为这是您实际读取的流。FileStream和CryptoStream的长度不同,因此您无法交换它们。然而,CryptoStream也是非寻道的,这意味着它无法寻道流的结尾,并且使用.Length将抛出NotImplementedException。因此,对于像CryptoStream这样的非寻道流,您必须使用ReadByte()一次读取一个字节,直到流表示已完成。

您使用了错误的流长度进行解密,而不是fsInput.length。您应该使用cs.length,因为这是您实际读取的流。FileStream和CryptoStream的长度不同,因此您无法交换它们。然而,CryptoStream也是非寻道的,这意味着它无法寻道流的结尾,并且使用.Length将抛出NotImplementedException。因此,对于像CryptoStream这样的非寻道流,您必须使用ReadByte()一次读取一个字节,直到流显示它已完成。

在加密文件之前,我在末尾添加了一些字节。在解密字节数组之后,我找到了这个字节来精确地获取文件长度。

在加密文件之前,我在末尾添加了一些字节。在解密字节数组之后,我找到这个字节来精确获取文件长度。

默认填充为PKCS#7
PaddingMode.PKCS7
非空填充。MSDN上的页面。请看“尼尔·亨比答案”。@扎夫你完全正确,我更正了我的答案。但我认为使用
BinaryReader
一次读取所有内容比尼尔·亨比建议的
ReadByte()
性能更好。@zaph解密的
字节中的零不是来自PKCS 7填充,而是来自
byte[]bytes=新字节[fsInput.Length]fsInput.Length
实际上太大了。我更正了我的回答以反映这一方面。我的回答还有错误吗?(我愿意学习。)注意,
fsInput.Length
在加密和解密在距离和时间上分开的一般情况下是未知的。如果为加密和解密指定相同的填充选项,并对解密的最后字节调用final,则解密的数据将与加密的普通数据匹配。不需要摆弄。如果创建的缓冲区大于解密数据的大小,请将缓冲区截断为解密数据的报告大小。@haindl我已经测试了您的代码。这工作做得很好。BinaryReader的ReadBytes函数精确返回字节长度。默认填充为PKCS#7
PaddingMode。PKCS7
非空填充。MSDN上的页面。请看“尼尔·亨比答案”。@扎夫你完全正确,我更正了我的答案。但我认为使用
BinaryReader
一次读取所有内容比尼尔·亨比建议的
ReadByte()
性能更好。@zaph解密的
字节中的零不是来自PKCS 7填充,而是来自
byte[]bytes=新字节[fsInput.Length]fsInput.Length
实际上太大了。我更正了我的回答以反映这一方面。我的回答还有错误吗?(我愿意学习。)注意,
fsInput.Length
在加密和解密在距离和时间上分开的一般情况下是未知的。如果为加密和解密指定相同的填充选项,并对解密的最后字节调用final,则解密的数据将与加密的普通数据匹配。不需要摆弄。如果创建的缓冲区大于解密数据的大小,请将缓冲区截断为解密数据的报告大小。@haindl我已经测试了您的代码。这工作做得很好。BinaryReader的ReadBytes函数精确返回字节长度。
new BinaryReader(cs)。ReadBytes((int)fsInput.length)
将立即读取正确的字节数。对于Office文档的典型文件大小,这应该比一次读取一个字节有更好的性能。我使用cs.Length,但它抛出异常,cs.Length无法访问。这是真的。当我比较加密后和解密前的文件流长度时,它是不同的。我想读很多字节以提高加密、解密大文件时的速度。只有在ecrypt、decypt word、powerpoint文件和其他文件正常工作时才会出现此错误。
new BinaryReader(cs)。ReadBytes((int)fsInput.Length)
将立即读取正确的字节数。它的性能应该远远好于