【问题标题】:File Encryption and Decryption issue文件加密和解密问题
【发布时间】:2010-07-11 08:16:15
【问题描述】:

我一直在尝试在 VC# Express 2010 中加密和解密文件。

我看到的所有教程和文档都需要两个FileStreams 来加密文件——一个用于读取未加密版本,另一个用于加密。当我实际编写代码时,它一直抛出一个错误,告诉我它无法打开文件,因为它是由输出文件流中的另一个进程打开的。

我假设这是因为文件是由输入文件流打开的。所以这意味着我必须指定一个不同的文件名?所以即使在操作成功之后,我现在也会在目录中拥有原始的未加密文件和一个单独的加密版本?这不是违背要点吗?还是我在这里做错了什么?我的代码是这样的...

public string filename = "test.xml";
using (FileStream input = new FileStream(filename, FileMode.Open, FileAccess.Read))
using (FileStream output = new FileStram(filename, FileMode.Open, FileAccess.Write))
using (....all the crypto stream and transform stuff...)
{
    ...do the encryption....
}

【问题讨论】:

    标签: c# file-io cryptography


    【解决方案1】:

    你是对的,但它并没有打破这一点。 (流式)加密 API 旨在从 Src 加密到 Dst。考虑在通过网络发送/接收时加密输出等。这使它们保持简单,因为它们应该是。

    您对 Src 和 Dst 使用相同的文件会使问题复杂化。这并非完全不可能,但就像在自身上复制文件一样,它需要格外小心。

    请考虑,一般来说,加密会增加文件大小。因此,就地加密文件是不安全的。解密可能是,但我不会冒险。

    您需要的是一个临时文件和完成后的重命名操作。

    【讨论】:

    • 这涉及到另一个问题:如果攻击者可以访问您正在运行的计算机,您的文件就是不安全的。如果你想加密文件,你必须确保未加密的字节永远不会接触到硬盘。如果你用word打开你的超级秘密商业计划,各种临时文件都会被写入你永远无法清理的驱动器。如果你想保护一个文件,你真的需要整个驱动器加密。
    【解决方案2】:

    在您的示例中,您不能为同一文件的输入和输出创建单独的文件流,但您可以创建一个可读写的句柄。 FileAccess 枚举具有 flags 属性,因此您只需说 var handle = new FileStream(filename, FileAccess.Read | FileAccess.Write); 明显的缺点是,如果您的加密未成功完成,您将丢失数据。

    不过,我建议为输出设置一个单独的文件,至少这样在程序意外中断时不会丢失数据。如果加密成功完成,则删除原始文件并用原始文件名重命名加密文件。

    【讨论】:

    • 其他文件加密应用程序是这样工作的吗?在加密操作后,我从未见过未加密源的残留副本。我应该输出到一个临时文件....创建一个 if 语句来监视操作是否成功,然后如果成功,则删除原始文件并重命名临时文件以替换它?这样可以保证数据不丢失?
    • 这就是过去大多数应用程序的工作方式。我现在不确定。临时文件通常会被设置为隐藏,并且可能位于磁盘上的临时文件夹中。文件系统加密中使用了相同的想法 - 如果您将文件移动到加密文件系统,则在移动成功完成之前不会删除原始文件。您必须以某种方式保留原始数据,直到您知道加密已成功完成,因为它可能是简单的事情,例如内存不足或磁盘已满导致您的应用程序失败。
    • 如果您真的需要回收现有文件,我建议您以固定的块大小读取它(比如 4096kb - 可以轻松放入内存的东西)。当且仅当每个块成功完成时,才覆盖原始块,但如果没有,则将文件加密的位置以及有助于恢复它的任何潜在数据输出到日志文件。 (即初始化向量、键、错误原因)。
    【解决方案3】:

    使用File.ReadAllBytes。然后这些字节发送到您的加密器,必须工作。

    【讨论】:

      【解决方案4】:

      还有一个参数可以指定是否允许其他进程读取或写入文件。

      openFile 是代表文件名的字符串。

      using (FileStream fileIn = new FileStream(openFile, FileMode.Open, FileAccess.Read, FileShare.Write))
      using (FileStream fileOut = new FileStream(openFile, FileMode.Open, FileAccess.Write, FileShare.Open))
      

      这样,您可以读取和写入同一个文件。

      while (myfileStream.Position < fileLength)
      {   
          fileIn.Read(buffer, 0, 51200);    
          buffer = encrypt(buffer);    
          fileOut.Write(buffer, 0, 51200);                      
      }
      

      虽然这很简单,而且您不必写入临时文件或移动/重命名等,但这可能非常危险,因为如果加密由于某种原因突然中断,您将丢失数据!

      另外,encrypt 函数是我实现的。可以使用AesCryptoServiceProviderCryptoStream :)

      【讨论】:

      • 我认为你的 while 条件被打破了。但更糟糕的是,fileOut的位置会开始超过fileIn的位置,你的文件就会乱码无法修复。
      • @Henk:是的。你是对的。但问题出在移动设备中,如果有人想加密一个 500MB 的文件,由于资源限制,创建另一个 500MB 的文件是不可能的。我承认这是危险的,并且可能有问题,但是对于资源受限的设备来说,必须要做这样的事情,对吧?还有其他方法吗? :)
      • OQ 没有提到移动,所以你在这里扩展问题。您可以通过在混合中添加压缩来解决没有临时文件的情况。但这并不能保证(如果 Src 文件的压缩性不够,即已经压缩,它将中断)。
      • @Henk:谢谢! :) 我将针对此事提出另一个问题 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-10
      • 2011-04-11
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-22
      相关资源
      最近更新 更多