/// 將加密的的資料,解密後還原 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
