代码之家  ›  专栏  ›  技术社区  ›  Sk93

对大型文件使用Rijndael加密

  •  7
  • Sk93  · 技术社区  · 14 年前

    在这种情况下,我需要安全地加密/解密n长度的文件,理想情况下使用rijndael,但绝对是256bit加密。

    我以前玩过加密技术,很高兴地对字符串和字节数组进行了加密/解密。 但是,因为我不知道文件的大小(而且讨论中的文件可能非常大(~2.5GB)是非常可行的),所以我不能像以前那样将它们加载到字节数组中并按单个绑定进行加密/解密。

    所以,在谷歌上读了几遍之后,我知道答案是对文件进行分块加密和解密,所以我生成了以下代码:

    private static void Enc(string decryptedFileName, string encryptedFileName)
    {            
       FileStream fsOutput = File.OpenWrite(encryptedFileName);
       FileStream fsInput = File.OpenRead(decryptedFileName);
    
       byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
    
       fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
       fsOutput.Write(IVBytes, 0, 16);
    
       RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC};
       ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordDB.GetBytes(256 / 8), IVBytes);
       CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write);
    
       for (long i = 0; i < fsInput.Length; i += chunkSize)
       {
          byte[] chunkData = new byte[chunkSize];
          fsInput.Read(chunkData, 0, chunkSize);
          cryptoStream.Write(chunkData, 0, chunkData.Length);
       }
       cryptoStream.Close();
       fsInput.Close();
       fsInput.Dispose();
       cryptoStream.Dispose();
    }
    
    private static void Dec(string encryptedFileName, string decryptedFileName)
    {
        FileStream fsInput = File.OpenRead(encryptedFileName);
        FileStream fsOutput = File.OpenWrite(decryptedFileName);
    
        byte[] buffer = new byte[8];
        fsInput.Read(buffer, 0, 8);
    
        long fileLength = BitConverter.ToInt64(buffer, 0);
    
        byte[] IVBytes = new byte[16];
        fsInput.Read(IVBytes, 0, 16);
    
        RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC };
        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordDB.GetBytes(256 / 8), IVBytes);
        CryptoStream cryptoStream = new CryptoStream(fsOutput,decryptor,CryptoStreamMode.Write);
    
        for (long i = 0; i < fsInput.Length; i += chunkSize)
        {
            byte[] chunkData = new byte[chunkSize];
            fsInput.Read(chunkData, 0, chunkSize);
            cryptoStream.Write(chunkData, 0, chunkData.Length);
        }
        cryptoStream.Close();
        cryptoStream.Dispose();
        fsInput.Close();
        fsInput.Dispose();                      
    } 
    

    这一切“看起来”对我好,但可悲的是,看起来似乎是欺骗!

    加密工作正常,但在解密过程中,“cryptostream.close()”方法引发以下异常:

    System.Security.Cryptography.Cryptographic例外 was unmanaged message=“padding is 无效,无法删除。“
    source=“mscorlib”堆栈跟踪: at system.security.cryptography.rijndaelManagedTransform.decryptData(byte[] 输入缓冲区,Int32输入缓冲区,Int32 输入计数,字节[]&输出缓冲区, Int32输出偏移,填充模式 paddingmode,布尔flast) at system.security.cryptography.rijndaelManagedTransform.TransformFinalBlock(byte[] 输入缓冲区,Int32输入缓冲区,Int32 输入计数) 在System.Security.Cryptography.CryptoStream.FlushFinalBlock()上 at system.security.cryptography.cryptostream.dispose(布尔值 处置) 在system.io.stream.close()上

    未加密的文件大小似乎与预期的文件大小不匹配(从大约8个字节到大约60个字节)。

    我通过修改rijndaelManaged对象创建行以包含填充类型来“修复”异常,如下所示:

    RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.None };
    

    但文件大小仍然不匹配,可以预见,新未加密的文件是胡扯!

    我承认我现在不在加密/解密的舒适区,这可能是一个新手的错误-但我不能发现它!

    如有任何帮助,我们将不胜感激!

    3 回复  |  直到 8 年前
        1
  •  5
  •   Sk93    14 年前

    问题是我使用了:

    passwordDB.GetBytes(256 / 8)
    

    在加密和解密方法中的RijndaelManaged对象的构造函数中,在尝试解密之前,我没有重新初始化PasswordDB对象。

    解决方案是在enc和dec方法的第一行中简单地包括passworddb对象的构造,如下所示:

            private static void Enc(string decryptedFileName, string encryptedFileName)
            {
                PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
                byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
    
                using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
                {
                    using(FileStream fsInput = File.OpenRead(decryptedFileName))
                    {
                        byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
    
                        fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
                        fsOutput.Write(IVBytes, 0, 16);
    
                        RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
                        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);                   
    
                        using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
                        {
                            for (long i = 0; i < fsInput.Length; i += chunkSize)
                            {
                                byte[] chunkData = new byte[chunkSize];
                                int bytesRead = 0;
                                while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                                {
                                    if (bytesRead != 16)
                                    {
                                        for (int x = bytesRead - 1; x < chunkSize; x++)
                                        {
                                            chunkData[x] = 0;
                                        }
                                    }
                                    cryptoStream.Write(chunkData, 0, chunkSize);
                                }
                            }
                            cryptoStream.FlushFinalBlock();
                        }
                    }
                }            
            }
    
            private static void Dec(string encryptedFileName, string decryptedFileName)
            {
                PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
                byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
    
                using (FileStream fsInput = File.OpenRead(encryptedFileName))
                {
                    using (FileStream fsOutput = File.OpenWrite(decryptedFileName))
                    {
                        byte[] buffer = new byte[8];
                        fsInput.Read(buffer, 0, 8);
    
                        long fileLength = BitConverter.ToInt64(buffer, 0);
    
                        byte[] IVBytes = new byte[16];
                        fsInput.Read(IVBytes, 0, 16);
    
    
                        RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
                        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordBytes, IVBytes);
    
                        using (CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
                        {
                            for (long i = 0; i < fsInput.Length; i += chunkSize)
                            {
                                byte[] chunkData = new byte[chunkSize];
                                int bytesRead = 0;
                                while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                                {
                                    cryptoStream.Write(chunkData, 0, bytesRead);
                                }
                            }
                        }
                    }
                }
            }
    

    知道那一定是个小学生的错误:p

        2
  •  3
  •   GvS    14 年前

    这个 Stream.Read 方法,返回实际从流中读取的字节数。

    您应该使用这个返回值作为下一行的write方法中的最后一个参数。

    我的代码如下:

    byte[] chunkData = new byte[chunkSize];   
    var bytesRead = 0;
    while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
    {
        cryptoStream.Write(chunkData, 0, bytesRead);
    }
    
        3
  •  0
  •   Simon Egli    8 年前

    有一个加密/解密流的CryptoStream类