【问题标题】:Why can't I combine encryption with file storage, when they work individually?当它们单独工作时,为什么我不能将加密与文件存储结合起来?
【发布时间】:2011-02-22 21:57:26
【问题描述】:

我对 C# 很陌生,所以请原谅我。很长一段时间以来,我一直在用这个头撞墙,但找不到解决方案。可能这是非常明显的事情。

就这样吧:我正在将一些对象属性写入文件。首先,我将属性转换为字节数组,然后将整个数组(对于“一个对象”)放在一起,并使用 Aes 通过 MemoryStream 对其进行加密。我知道序列化和其他可能性,但我真的需要这样做。在其他一些方法中,我以块(“对象”)读取此文件,对其进行解密,然后从字节数组中重建对象属性。问题是只有第一条记录(“对象”)才能正常/正确地解密和重建。所有其他人的数据都搞砸了(int 得到值 48464 而不是 2,String 显示奇数​​符号,double 是 -3.16...E-161 而不是 20...)。

我也不知道为什么。我尝试了我能想到的一切。如果我注释掉加密和解密一切正常,那么写和读都不是问题。如果我将用于解密和重建对象的代码放在加密代码的下方(以便我解密写入的数据块),它会正确地解密和重建所有内容,因此解密和重建应该不是问题。但是,当它全部放在一起时,它就会变得一团糟。我真的迷路了。

请不要专注于我处理数据的方式,它现在真的不重要,我这样做是有原因的。

这是保存到文件的完整代码:

//constant for setting inUse
        byte setInUse = 0x80; //1000 0000

        //constant for adding spaces to name (string)
        byte[] space = Encoding.UTF8.GetBytes(" ");

        //result
        byte[] data = new byte[32];

        //setup encryption (AES)
        SymmetricAlgorithm aes = Aes.Create();
        byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
        byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
        aes.Padding = PaddingMode.None;
        ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);        

        //setup file stream for saving data
        FileStream fStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false);
        if(writeIndex != 0)
            fStream.Position = writeIndex +1;

        fStream.Position = 0; //delete me

        foreach(Article article in articles)
        {
           if(article.MyIsNew)
           {
               article.MyInUseChanged = false;
               article.MyPriceChanged = false;

               //convert article to byte array
               //id
               byte[] id = BitConverter.GetBytes(Convert.ToUInt16(article.MyId));   
               //in use
               if (article.MyInUse)
                   id[0] = (byte)( id[0] | setInUse);

               data[0] = id[0];
               data[1] = id[1];

               //stock
               byte[] stock = BitConverter.GetBytes(article.MyStock);
               data[2] = stock[0];
               data[3] = stock[1];
               data[4] = stock[2];
               data[5] = stock[3];
               data[6] = stock[4];
               data[7] = stock[5];
               data[8] = stock[6];
               data[9] = stock[7];

               //name
               byte[] name = Encoding.UTF8.GetBytes(article.MyName);
               int counter = 10;
               for (int i = 0; i < name.Length; i++)
               {
                   data[counter] = name[i];
                   counter++;
               }

               //adding spaces
               int numToAdd = 22-name.Length;
               for (int i = 0; i < numToAdd; i++)
               {
                   data[counter] = space[0];
               }

               //encrypt
               MemoryStream m = new MemoryStream();
               using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
                   c.Write(data, 0, data.Length);
               byte[] original = new byte[32];
               original = m.ToArray();
               fStream.Write(original, 0, original.Length);

           }
           else if (article.MyInUseChanged)
           {

           }

           if (article.MyPriceChanged)
           {

           }

        }   
        fStream.Flush();
        fStream.Close();
        fStream.Dispose();

这是加载的全部代码:

String fileName = path + "\\articles";

        //load data
        if (File.Exists(fileName))
        {
            FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);

            //setup encryption (AES)
            SymmetricAlgorithm aes = Aes.Create();
            byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
            byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
            aes.Padding = PaddingMode.None;
            ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);

            //constant for extracting inUse
            byte inUseConst = 0x80;

            //constant for extracting id
            byte idConst = 0x7F;

            byte[] idArray = new byte[2];

            //reading & constructing & adding articles to the list
            int numBytesToRead = (int)fStream.Length;
            while (numBytesToRead > 0)
            {
                byte[] original = new byte[32];
                byte[] data = new byte[32];

                int len = fStream.Read(original, 0, 32);
                numBytesToRead -= 32;
                if (len == 0 || len != 32)
                {
                    MessageBox.Show("Error while loading articles");
                    break;
                }
                long pos = fStream.Position; //delete me
                //decrypt
                MemoryStream m = new MemoryStream();
                using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write))
                    c.Write(original, 0, original.Length);
                data = m.ToArray();

                //constructing object - article
                //inUse
                byte inUseCalc = (byte)(data[0] & inUseConst);
                bool inUse = false;
                if (inUseCalc != 0)
                {
                    inUse = true;
                }

                //id
                data[0] = (byte)(data[0] & idConst);
                int id = (int)(BitConverter.ToUInt16(data, 0));

                //stock
                double stock = BitConverter.ToDouble(data, 2);

                //name
                String name = Encoding.UTF8.GetString(data, 10, 22);

                Article article = new Article(id, 10, name, inUse, stock);
                articles.Add(article);

有些事情不是最佳的,因为我改变了很多只是为了找到解决方案。有些事情(比如转换为 uInt16 和使用 'or' 等)部分是因为压缩。

请帮我解决这个问题,再次请不要专注于我处理数据或建议我使用序列化或二进制编写器或类似的东西,我真的有我的理由。

我真的指望你,因为我完全没有想法。谢谢大家宝贵的时间和答案。

【问题讨论】:

  • 您可能要考虑现在更改您的密钥...
  • @Greg Buehler 这只是硬编码的密钥,我将一直使用它,直到我让所有其他事情都正常工作为止。我当然会改,而且绝对不会硬编码。

标签: c# encryption writing


【解决方案1】:

实际问题是您每次都在重新创建输出CryptoStream。显然这样做是在修改加密器中的某些状态,这会导致每条记录的前几个字节的输出与解密器的预期不同。

如果您在循环之外构造加密CryptoStream,直接写入输出文件或写入单个MemoryStream,问题就消失了(这次我实际测试了它...)

using (var fStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
using (var m = new MemoryStream())
using (var c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
{
    foreach (Article article in articles)
    {
        // ...
        c.Write(data, 0, data.Length);
        byte[] original = new byte[32];
        original = m.ToArray();
        m.Position = 0;
        fStream.Write(original, 0, original.Length);
    }
}

【讨论】:

  • 感谢您的帮助。似乎这解决了问题,或者如果我在循环中而不是之前创建了一个新的解密器。好吧,我想我现在有一个不同的问题,因为我必须只能解密一个数据块,但除非我为每个块创建新的解密器,否则我不能这样做,我认为这是非常低效的。此外,我必须只能在指定位置写入一大块数据,这又会产生问题。无论如何,非常感谢。
【解决方案2】:

CryptoStream 与任何其他流的使用方式相同,也就是说,如果将其包裹在 FileStream 周围并将其转换回 Stream,则无法区分。

在您的解密代码中,您正在写入CryptoStream,而不是从中读取。你可能想要更多这样的东西:

using (Stream c = new CryptoStream(fStream, decryptor, CryptoStreamMode.Read))
{
    while (numBytesToRead > 0)
    {
        byte[] original = new byte[32];
        byte[] data = new byte[32];

        int len = c.Read(original, 0, 32);
        numBytesToRead -= 32;

        // and so on
    }
}

(根据要求,我不会对您的一般方法发表评论。但是,我建议您尽可能将整个CryptoStream 读入内存(立即将其写入MemoryStream)并关闭原始文件并尽快流式传输对象。)

【讨论】:

  • 正如我所说,如果我将用于解密的代码放在加密代码的下方,它会按照应有的方式工作(它会解密并正确重建对象)。我不使用 CryptoStream 包装 FileStream,因为我需要使用 Seek 和 Position 功能。
  • @Ben 我可以保证您不能通过写入CryptoStream 来解密数据 - 当您将变量放在一起时,您必须初始化它们。此外,您无法搜索加密数据是有原因的(因为它会破坏解密状态),因此您需要将整个文件解密为 MemoryStream 并在其中搜索。
  • 您可以通过写入内存流来解密数据。您可以使用我的代码进行加密和解密,并在其中输入您自己的数据,您将看到。我将注释“解密”之后的整个代码复制(并因为名称相同而重命名)到整个加密代码下面的代码。它也奏效了。
  • @Ben 好的,所以我实际测试了代码(弥补了未包含的位),发现当您继续重新创建 CryptoStream 时加密无法正常工作。我已经在单独的答案中回答了。我仍然坚持我的建议,即应该始终如一地使用流,但如果你真的有需要,那么你别无选择。此外,我无法重现“当代码在一起时它可以工作”的一面。
猜你喜欢
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多