2011-02-22 46 views
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並使用「或」等)部分原因是壓縮。

請幫我解決這個問題,請不要關注我對數據的處理或者建議我使用序列化或二進制編寫器或類似的東西,我真的有我的理由。

我真的相信你,因爲我完全沒有想法。謝謝大家寶貴的時間和答案。

+0

你可能要考慮現在更換你的鑰匙... – 2011-02-22 23:55:56

+0

@Greg Buehler這是我用過的硬鑰匙,直到我得到所有其他工作。當然我會改變它,它絕對不會被硬編碼。 – Ben 2011-02-23 08:41:09

回答

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); 
    } 
} 
+0

謝謝你的幫助。看來這解決了問題,或者如果我在循環中創建一個新的解密器,而不是之前。好吧,我想現在我有一個不同的問題,因爲我必須能夠解密只有一塊數據,但我不能這樣做,除非我爲每個塊創建新的解密器,這是我認爲效率很低。另外,我必須能夠在指定的位置只寫入一大塊數據,這又會產生問題。無論如何非常感謝。 – Ben 2011-02-23 10:20:58

0

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),並儘快關閉原始文件和流對象。)

+0

正如我所說,如果我把我的解密代碼放在加密代碼的下面,它就會按照它的方式工作(它解密並正確重建對象)。我不會用CryptoStream包裝FileStream,因爲我需要使用功能Seek and Position。 – Ben 2011-02-22 22:16:41

+0

@Ben我可以保證你不能通過*寫*到CryptoStream來解密數據 - 當你把它們放在一起時,你必須保留變量的初始化。另外,有一個原因是你無法通過加密數據尋找(因爲它破壞了解密狀態),所以你需要將整個文件解密成一個「MemoryStream」並在其中尋找。 – Zooba 2011-02-22 22:39:35

+0

您可以通過寫入內存流來解密數據。您可以使用我的代碼進行加密和解密,並在其中輸入您自己的數據,您將看到。而且,我將整個代碼複製(並將其中的一些內容重命名爲相同名稱),解密後的整個代碼添加到整個加密代碼下面的代碼中。它也起作用。 – Ben 2011-02-22 23:05:28

相關問題