3DES加解密

  • 4675
  • 0
  • 2013-06-02

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字串就好。

 

參考資料來源

  1. Base64編碼, http://blog.wahahajk.com/2008/06/base64.html
  2. 3DES演算法, http://zh.wikipedia.org/wiki/3DES