/// 將加密的的資料,解密後還原 Object 原Class /// Class type /// 序列化加密資料 /// private key public static T DecryptObject(byte[] EncryptData, string privkey) { using (MemoryStream memory = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); var decrypt = RSADecrypt(EncryptData, privkey); memory.Write(decrypt, 0, decrypt.Length); memory.Seek(0, SeekOrigin.Begin); return (T)formatter.Deserialize(memory); } }
接著 DecryptObject 用來將加密的資料解密後,反序列換並且轉成原本 Object Classs Type 的 Mehtod 。最後有呼叫範例提供給各位參考。
其中 RSADecrypt 為 RSA 解密的 Mehtod。
public static byte[] RSADecrypt(byte[] DataToDecrypt, string privkey) { try { byte[] decryptedData; using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider()) { RSA.FromXmlString(privkey); int DecryptedSize = 0; if (DataToDecrypt.Length > 128) { ArrayList arrDecrypteToTxt = new ArrayList(); decryptedData = DecryptedDataMethod(RSA, DataToDecrypt, arrDecrypteToTxt, DecryptedSize); } else { decryptedData = RSA.Decrypt(DataToDecrypt, false); } } return decryptedData; } catch (CryptographicException e) { Console.WriteLine(e.ToString()); return null; } }因為解密也有Size的問題,所以 array size 超過某一定大小要分段解密。
/// RSA分段解密 /// RSA物件 /// 要解密的位元組 /// 存放每組解密後的字串陣列 /// 存放解密後的位元組資料長度 private static byte[] DecryptedDataMethod(RSACryptoServiceProvider rsa, byte[] orgData, ArrayList arrDecrypteToTxt, int DecryptedSize) { byte[] DecryptedData = null; try { //解密後位元組再轉成64位數編碼的字串 string strDecrypteToTxt = ""; //暫存要解密的位元組容器 byte[] temp = null; //解密後的位元組 byte[] tempDecryptedData = null; //若要解密的資料位元長度>128 if (orgData.Length > 128) { temp = new byte[128]; //再把傳進來的位元組從0-128跑迴圈,一個一個賦予temp每個位元值 for (int i = 0; i < 128; i++) { temp[i] = orgData[i]; } } else { temp = orgData; } //tempDecryptedData存放解密後的位元組資料 tempDecryptedData = rsa.Decrypt(temp, false); //解密後位元組資料長度 DecryptedSize += tempDecryptedData.Length; //再把tempDecryptedData位元組資料轉成64編碼字串 strDecrypteToTxt = Convert.ToBase64String(tempDecryptedData); //把字串add到陣列裡 arrDecrypteToTxt.Add(strDecrypteToTxt); //再判斷一次傳進來要解密的位元組資料長度 if (orgData.Length > 128) { //again存放要解密的位元組資料長度-128 byte[] again = new byte[orgData.Length - 128]; int j = 0; //傳進來要解密的位元組資料從128位址開始跑迴圈 for (int i = 128; i < orgData.Length; i++) { //把傳進來要解密的位元組資料指派給again again[j] = orgData[i]; j++; } //遞回呼叫DecryptedDataMethod函式,傳入rsa,要再解密位元組資料,存放每組解密資料轉成64編碼的字串陣列 return DecryptedDataMethod(rsa, again, arrDecrypteToTxt, DecryptedSize); } else { //若傳進來要解密的位元組長度小於128,代表不用遞回解密了 //建立一個新的位元組物件,長度是(存放每組解密資料轉成64編碼的字串陣列的位元數)*117, 117是代表解密出來的位元組長度,是固定的 //DecryptedData = new byte[arrDecrypteToTxt.Count * 117]; DecryptedData = new byte[DecryptedSize]; //宣告解密位元組,預設null Byte[] btFromBase64 = null; int p = 1;//計算目前位置倍數 int currentAddr = 0;//目前存放資料的位元位置 int k = 0; double dk = 0; dk = Math.Floor((double)(DecryptedSize / 117));//解密後位元組資料長度/117,無條件捨去求整數部分 k = (int)dk; int n = 0;//與k比較用 //跑陣列回圈,每個陣列位元存放每個分段加密後的64編碼字串 foreach (string strArr in arrDecrypteToTxt) { if (n < k) { //再把字串從64位元編碼轉換為8位元的位元組 btFromBase64 = Convert.FromBase64String(strArr); //將位元組資料複製到目的地位元組陣列eDecryptedData,並指定從目的地陣列n*117的位置貼上 btFromBase64.CopyTo(DecryptedData, n * 117); currentAddr = p * 117;//目前資料已存到的位置 p++; n++; } else { //再把字串從64位元編碼轉換為8位元的位元組 btFromBase64 = Convert.FromBase64String(strArr); //將位元組資料複製到目的地位元組陣列eDecryptedData,並指定從目的地陣列currentAddr的位置貼上 btFromBase64.CopyTo(DecryptedData, currentAddr); } } } } catch (Exception ex) { throw new Exception(ex.Message); } //最後回傳分段加密資料的位元組 return DecryptedData; //The End }分享分段加解密的網友很貼心了下了註解XD
有了以上的程式,我們就可以把整個 Object 序列化->加密 ->解密->反序列化 的情境完成。
以下有一個範例程式,就那上篇的 Person Class 做示範。
static void Main(string[] args) { //生產 RSA Key CspParameters csp = new CspParameters(); csp.KeyContainerName = "FredJama"; csp.Flags = CspProviderFlags.UseMachineKeyStore; csp.ProviderType = 1; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024, csp); string PrivateKey = rsa.ToXmlString(true); string PublicKey = rsa.ToXmlString(false); Person person = new Person("Fred", "DJ", "Jama"); byte[] encryptDate = null; Person decryptPerson = null; Console.WriteLine("原始內容"+person.ToString()); //序列化加密 encryptDate = EncryptObject(person, PublicKey); string encrypdateString = ByteListToString(encryptDate); Console.WriteLine("加密內容:"+ encrypdateString); //解密後反序列化 decryptPerson = DecryptObject(encryptDate, PrivateKey); Console.WriteLine("解密內容:" + decryptPerson.ToString()); Console.ReadLine(); }
此段測試程式前半段是簡單生出一個 rsa key 用來加解密。執行結果如下圖:
參考資料:
http://www.dotblogs.com.tw/yc421206/archive/2012/06/25/73041.aspx
http://www.dotblogs.com.tw/yc421206/archive/2012/06/26/73057.aspx
http://www.dotblogs.com.tw/yc421206/archive/2012/06/25/73041.aspxRSA 分段加密
http://www.wretch.cc/blog/yanan222/12339145
沒有留言:
張貼留言