3DES加解密
有的時候在傳遞一些機敏性的資料,通常會需要進行一些加解密,而微軟在.Net Framework中加解密的部份我認為很方便使用,而不論是加密還是解密,其中大概只有一行的差別而已。
目前因為工作上的需求,因此,我採用了3DES這個加密技術,而這個加密技術在過去CPU運算能力不佳的時候,並不算太常使用,但是現在就連手機都有強勁的運算能力了,故採用這個加解密演算法也算是稀鬆平常了。
3DES
是將ANSU X3.92中定義的資料加密演算法(DEA)做了三次重覆的操作,而且演算概略如下:
密文=Ek3(Dk2(Ek1(本文)))
下標符號k即代表金鑰;而K1代表第一隻金鑰,K2和K3以此類推。首先,先將本文以第一把金鑰k1進行加密,接著,再利用第二把金鑰K2進行解密,最後再以第三把金鑰k3再進行一次加密,每次加解密都採用DEA。
本文=Dk1(Ek2(Dk3(密文)))
要解密就要逆著過來做;首先先以第三把金鑰k3先進行解密,完成之後在以第二把金鑰進行加密,最後則是以第一把金鑰k1進行解密,如此就能解出本文來。
DES本質上是一種資料塊加密,亦即,該演算法會將資料以64bit為一塊進行切割,再對這些資料塊進行加解密的處理。
金鑰選擇
有三種金鑰模式:
1. 三把金鑰都不相同
2. k1和k2不同,但k1和K3相同
3. 三把金鑰都相同
以第一種模式的加密強度為最高,一共有3*56=168個獨立金鑰位元數。第二種尚可,共有2*56=112個金鑰位元數,第三種與一般DES沒有差別,因為依演算法來說,加密過程的第一動和第二動相互抵消了,不但加密強度沒增加還白白浪費一堆系統資源。
插花的-Base64編碼
本質上Base64和3DES是沒啥關係的,但是為何要特別介紹這個呢?
主要是因為在傳遞(Copy/Past)金鑰和向量給別人時,一堆二進化的東西很難傳遞,故採用Base64編碼讓金鑰和向量能夠簡短一些。
Base64是採用2的次方來代表ASCII字元,通常用在電子郵件(EMail)的傳輸編碼上。字元有三組:
1. A~Z
2. a~z
3. 0~9
這樣一共有62個字元,對於名稱64來說還缺了兩個,而遺落的那兩個就是依數字符號在不同系統中而有所不同。
Base64的演算法如下:
一次取3個Byte(=24bit)放入緩衝區中,位處24bit前面的占高位元處,倘若資料不足24bit,則遇缺補0。每次取出6個bit,並且依照其值對應到Base64的字元組;
A~Za~z0~9+/
不斷的重覆上述的作業,直到所有資料都取用完畢。
如果取到最後剩兩個輸入數據,則在最後的結果加上1個"=";若剩一個則加上2個”=”。
程式碼
3DES實際加解密程式碼如下:
/// 將物件所有公有屬性加密
/// </summary>
/// <param name="obj">欲加密物件</param>
/// <returns>加密後的字串</returns>
public static string EnCryptoContent(object obj)
{
string strData = JsonConvert.SerializeObject(obj);
byte[] arContent = Encoding.UTF8.GetBytes(strData),
arResult = null;
var provider = new TripleDESCryptoServiceProvider()
{
Key =Convert.FromBase64String("~Base64編碼字串~"),
IV = Convert.FromBase64String("~Base64編碼字串~"),
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
ICryptoTransform ct = provider.CreateEncryptor();
arResult = ct.TransformFinalBlock(arContent, 0, arContent.Length);
strData = Convert.ToBase64String(arResult);
return strData;
}
由於我想要讓加密的內容是某一個POCO,故,我在加密時的第一個動作就是先將它Json序列化。
Key與IV可以採用Base64編碼的字串,或是其它編碼格式的字串,而我個人是比較喜歡Base64編碼格式的字串。
/// 解密字串
/// </summary>
/// <param name="content">欲解密的字串</param>
/// <returns>解密後的字串</returns>
public static string DeCryptoContent(string content)
{
string strData = string.Empty;
byte[] arContent = Convert.FromBase64String(content),
arResult = null;
var provider = new TripleDESCryptoServiceProvider() {
Key = Encoding.ASCII.GetBytes("~Base64編碼字串~"),
IV = Encoding.ASCII.GetBytes("~Base64編碼字串~"),
Mode=CipherMode.CBC,
Padding=PaddingMode.PKCS7
};
ICryptoTransform ct = provider.CreateDecryptor();
arResult = ct.TransformFinalBlock(arContent, 0, arContent.Length);
strData = Encoding.UTF8.GetString(arResult).Replace("\"",string.Empty);
return strData;
}
解密的部份我不直接解成POCO,而是由Client決定是否要轉回來成它要的POCO,或是保持原Json字串就好。
參考資料來源
- Base64編碼, http://blog.wahahajk.com/2008/06/base64.html
- 3DES演算法, http://zh.wikipedia.org/wiki/3DES