1

我对 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 和使用“或”等)部分是因为压缩。

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

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

4

2 回答 2

2

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);
    }
}
于 2011-02-22T23:52:54.477 回答
0

CryptoStream与任何其他流的使用方式相同,也就是说,如果将其包裹在 a 周围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入内存(立即将其写入 a MemoryStream)并尽快关闭原始文件和流对象。)

于 2011-02-22T22:08:00.807 回答