關於物件例項序列化加密解密並寫入檔案的技巧
阿新 • • 發佈:2019-02-01
最近在網上看見一些文章在討論如何將一個物件或物件的列表序列化並加密後儲存到檔案中的討論,有很多方法針對英文字串和數值型資料都能很好完成,但是加入中文,或者加入一些諸如DateTime型別的資料後,在反序列化或解密時總是會出現異常,不能正確執行,經過一天的測試和比對,發現要解決這個問題,要注意以下幾點
1、只討論TripleDES加密演算法,其它演算法也類似,金鑰Key和金鑰向量IV在加密和解密時應一致,就是說要使用和加密相同的Key和IV進行解密(有的文章說要Key和IV一致,沒有必要,也做不到!),KEY最好是24位元組,IV最好是8位元組(其他演算法根據演算法要求)。
2、加密處理順序:序列化-->加密-->寫入檔案。解密處理順序:讀取檔案-->解密-->反序列化
3、序列化後的結果要儲存到byte[ ]中,序列化時建議使用MemoryStream作為緩衝區,直接使用ToArray方法將位元組陣列返回,不要用任何EnCoding的GetBytes方法。
4、將序列化得到的位元組陣列直接進行加密,不要把它轉換成字串或者字元。
5、加密的結果也直接以位元組陣列的形式寫入檔案,不要使用BinaryWriter之類會自己加入一些編碼字元的寫入器。
1、只討論TripleDES加密演算法,其它演算法也類似,金鑰Key和金鑰向量IV在加密和解密時應一致,就是說要使用和加密相同的Key和IV進行解密(有的文章說要Key和IV一致,沒有必要,也做不到!),KEY最好是24位元組,IV最好是8位元組(其他演算法根據演算法要求)。
2、加密處理順序:序列化-->加密-->寫入檔案。解密處理順序:讀取檔案-->解密-->反序列化
3、序列化後的結果要儲存到byte[ ]中,序列化時建議使用MemoryStream作為緩衝區,直接使用ToArray方法將位元組陣列返回,不要用任何EnCoding的GetBytes方法。
4、將序列化得到的位元組陣列直接進行加密,不要把它轉換成字串或者字元。
5、加密的結果也直接以位元組陣列的形式寫入檔案,不要使用BinaryWriter之類會自己加入一些編碼字元的寫入器。
6、解密時也相同,直接用位元組陣列,不要有什麼轉換,一路byte[ ]到底,反序列化會自動將位元組資料轉換為合適的資料型別存入物件的對應欄位。
class TripleDESApplay { private TripleDESCryptoServiceProvider tp; public byte[] DESKey { get { return tp.Key; } set { if(value.Length<24) { throw new ArgumentException("金鑰Key的長度應該為24個位元組!"); } else { tp.Key = value.Take(24).ToArray(); } } } public byte[] DESIV { get { return tp.IV; } set { if(value.Length<8) { throw new ArgumentException("金鑰向量IV的長度應該為24個位元組!"); } else { tp.IV = value.Take(8).ToArray(); } } } public TripleDESApplay() { tp = new TripleDESCryptoServiceProvider(); tp.GenerateKey(); tp.GenerateIV(); } public TripleDESApplay(byte[] key,byte[] iv) { tp = new TripleDESCryptoServiceProvider(); DESKey = key; DESIV = iv; } /// <summary> /// 將位元組陣列進行加密,並返回加密後的位元組陣列 /// </summary> /// <param name="encryptBytes">要加密的位元組陣列</param> /// <returns>加密後的位元組陣列</returns> public byte[] EncryptBytes(byte[] encryptBytes) { byte[] result = null; try { //用來儲存加密結果的記憶體流 using (MemoryStream s = new MemoryStream()) { //建立加密流 using (CryptoStream cs = new CryptoStream(s, tp.CreateEncryptor(), CryptoStreamMode.Write)) { cs.Write(encryptBytes, 0, encryptBytes.Length); cs.FlushFinalBlock(); //根據Stream中加密的資料將其讀出到result位元組陣列中 //重置Stream的指標位置到開始處 s.Seek(0, SeekOrigin.Begin); //讀出Stream中的位元組到result位元組陣列 result = s.ToArray(); } } } catch(Exception e) { throw e; } return result; } /// <summary> /// 對字串進行加密,並返回加密後的位元組陣列 /// </summary> /// <param name="encryptString">要加密的字串</param> /// <returns>加密後的位元組陣列</returns> public byte[] EncryptString(string encryptString) { byte[] result = null; try { byte[] source = Encoding.Default.GetBytes(encryptString); result = EncryptBytes(source); } catch(Exception e) { throw e; } return result; } /// <summary> /// 將使用TripleDES演算法加密的位元組陣列解密 /// </summary> /// <param name="decryptBytes">加密過的位元組陣列</param> /// <returns>解密後的位元組陣列</returns> public byte[] DecryptBytes(byte[] decryptBytes) { byte[] result = null; try { using (MemoryStream s = new MemoryStream(decryptBytes, 0, decryptBytes.Length)) { using (CryptoStream cs = new CryptoStream(s, tp.CreateDecryptor(), CryptoStreamMode.Read)) { result = new byte[decryptBytes.Length]; cs.Read(result, 0, decryptBytes.Length); } } } catch(Exception e) { throw e; } return result; } /// <summary> /// 將使用TripleDES演算法加密後生成的位元組陣列解密,並返回解密後的字串 /// </summary> /// <param name="decryptBytes">加密後的位元組陣列</param> /// <returns>解密後的字串</returns> public string DecryptString(byte[] decryptBytes) { byte[] result = null; try { result = DecryptBytes(decryptBytes); } catch(Exception e) { throw e; } return Encoding.Default.GetString(result); } /// <summary> /// 序列化物件 /// </summary> /// <param name="obj">指定進行序列化的物件,物件應該具有[Serializable]特性</param> /// <returns>序列化後的位元組陣列</returns> public byte[] SerializeObject(object obj) { byte[] result = null; try { using (MemoryStream s = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(s, obj); result = s.ToArray(); } } catch(Exception e) { throw e; } return result; } /// <summary> /// 反序列化物件 /// </summary> /// <typeparam name="T">指定反序列化物件的型別</typeparam> /// <param name="seriaBytes">反序列化的位元組陣列</param> /// <returns>反序列化後的物件</returns> public T DeserializeObject<T>(byte[] seriaBytes) where T :class { T obj = default(T); try { using (MemoryStream s = new MemoryStream(seriaBytes, 0, seriaBytes.Length)) { BinaryFormatter bf = new BinaryFormatter(); obj = bf.Deserialize(s) as T; } } catch (Exception e) { throw e; } return obj; } /// <summary> /// 將位元組陣列寫入指定的檔案,如果檔案已經存在會覆蓋原來的檔案,如果檔案不存在會新建一個檔案 /// </summary> /// <param name="filename">指定檔案的完整路徑名</param> /// <param name="outBytes">要寫入的位元組陣列</param> public void WriteBytesToFile(string filename,byte[] outBytes) { try { using (FileStream fWriter = new FileStream(filename, FileMode.Create, FileAccess.Write)) { fWriter.Write(outBytes, 0, outBytes.Length); fWriter.Close(); } } catch (Exception e) { throw e; } } /// <summary> /// 從指定檔案讀取位元組陣列 /// </summary> /// <param name="filename">指定檔案的完整路徑名</param> /// <returns>位元組陣列</returns> public byte[] ReadBytesFromFile(string filename) { byte[] result = null; try { using (FileStream fReader = new FileStream(filename, FileMode.Open, FileAccess.Read)) { result = new byte[fReader.Length]; fReader.Read(result, 0, result.Length); } } catch (Exception e) { throw e; } return result; } }