为什么我不能将加密与文件存储结合起来,当它们单独工作时?

时间:2011-02-22 21:57:26

标签: c# encryption writing

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

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

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

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

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

在您的解密代码中,您正在写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)并且尽快关闭原始文件和流对象。)