對稱加密演算法在C#中的踩坑日常
前言
有幸接觸了一下傳說中的對稱加密演算法3DES
感覺這些加密演算法與我的工作是想去甚遠的,一般沒什麼機會接觸這些東西
今次瞭解了一下3DES這個對稱演算法
原理算不上明白,算是踩了C#中的一些坑吧
C#中對於金鑰的處理比較奇怪,花費了一晚上一早上的時間才弄明白
期間偷窺了不少C#的原始碼
下面由我娓娓道來
簡介
3DES演算法命名
定義演算法最早期的標準被放在ANS X9.52中並在1998年釋出並將其描述為三重資料加密演算法(簡稱TDEA),在ANSI X3.92中定義了該演算法的三個操作但是並沒有使用DES或者3DES,直到1999年釋出的FIPS PUB 46-3在正式命名三重資料加密演算法,大概在2004到2005的樣子才正式引入三重資料加密演算法,之前一直以TDEA存在著,也就是說TDEA就是3DES,但是沒有使用3DES作為標準術語。
基本邏輯
三重資料加密演算法使用包括金鑰K1,金鑰K2和金鑰約束K3,每一個包含56位不包含奇偶校驗,演算法實現公式如下:
ciphertext = EK3(DK2(EK1(plaintext)))
即
密文 = EK3(DK2(EK1(平文)))
用K1對資料進行加密,用K2對資料進行解密,用K3對資料再加密。
解密公式為如下:
plaintext = DK1(EK2(DK3(ciphertext)))
即
平文 = DK1(EK2(DK3(密文)))
用K3j對資料進行解密,用K2對資料進行加密,用K1對資料進行加密。每次加密都處理64位資料並形成一塊。
3DES加密選項
定義了三種金鑰選項。
(1)三個金鑰相互獨立。
(2)K1和K2金鑰獨立,但K1 = K3。
(3)三個金鑰相等。
金鑰選項1的強度最高,擁有3 x 56 = 168個獨立的金鑰位。
金鑰選項2的安全性稍低,擁有2 x 56 = 112個獨立的金鑰位。該選項比簡單的應用DES兩次的強度較高,即使用K1和K2,因為它可以防禦中途相遇攻擊。
金鑰選項3等同與DES,只有56個金鑰位。這個選項提供了與DES的相容性,因為第1和第2次DES操作相互抵消了。該選項不再為國家標準科技協會(NIST)所推薦,亦不為ISO/IEC 18033-3所支援。
C#實現
講真簡介裡用來湊字數的這些內容我其實沒怎麼看明白
C#中使用TripleDESCryptoServiceProvider
public static string DesEncrypt(string input, string key)
{
byte[] inputArray = Encoding.UTF8.GetBytes(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.Key = Encoding.UTF8.GetBytes(key);
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string DesDecrypt(string input, string key)
{
byte[] inputArray = Convert.FromBase64String(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.Key = Encoding.UTF8.GetBytes(key);
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return Encoding.UTF8.GetString(resultArray);
}
從下面原始碼中看出,該類接收的Key為16位或24位
然後對於這個Key,C#似乎有自己的處理方式
以下為個人理解:
這個24位的key會被處理成3個8位元組的獨立金鑰參與運算
當提供24位key時並沒有什麼不妥
但是當提供16位的key時 會把提供的key拆分成兩個塊(block) 並以第一個塊作為第三個塊組成一個24位的金鑰
如下:
輸入金鑰:49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 50, 51, 52, 53, 54, 55
實際使用:49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 50, 51, 52, 53, 54, 55, 49, 50, 51, 52, 53, 54, 55, 56
可以看出使用了前8位來進行後面8位的補全
這時候你可能要問,如果提供一個不是16位也不是24位的金鑰時會發生什麼
會拋異常
以上理解都是在.NetFramework中的體現
如果換到NetCore中,效果就又不一樣了
NetCore
在NetCore中不存在TripleDESCryptoServiceProvider
取而代之的是 TripleDES
所以此時我們的程式碼需要稍作修改
public static string DesEncrypt(string input, string key)
{
byte[] inputArray = Encoding.UTF8.GetBytes(input);
var tripleDES = TripleDES.Create();
var byteKey = Encoding.UTF8.GetBytes(key);
tripleDES.Key = byteKey;
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string DesDecrypt(string input, string key)
{
byte[] inputArray = Convert.FromBase64String(input);
var tripleDES = TripleDES.Create();
var byteKey = Encoding.UTF8.GetBytes(key);
tripleDES.Key = byteKey;
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
return Encoding.UTF8.GetString(resultArray);
}
NetCore中同樣要求我們提供24位的Key
但是不在相容16位的Key,如果你提供一個非24位的Key就會異常
不過沒關係,對於16位的Key我們可以自行處理一下
同理使用前8位補全後8位
public static string DesEncrypt(string input, string key)
{
byte[] inputArray = Encoding.UTF8.GetBytes(input);
var tripleDES = TripleDES.Create();
var byteKey = Encoding.UTF8.GetBytes(key);
//複製前8位補全後8位
byte[] allKey = new byte[24];
Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);
Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);
tripleDES.Key = allKey;
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string DesDecrypt(string input, string key)
{
byte[] inputArray = Convert.FromBase64String(input);
var tripleDES = TripleDES.Create();
var byteKey = Encoding.UTF8.GetBytes(key);
//複製前8位補全後8位
byte[] allKey = new byte[24];
Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);
Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);
tripleDES.Key = allKey;
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tripleDES.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
return Encoding.UTF8.GetString(resultArray);
}
至此就可以正常相容NetFramework的程式碼了
小結
至此寫下此文,也算是對3DES有了些許瞭解吧
需要記住
在.NET Core中利用3DES加密和解密必須要給出3個金鑰即24個位元組即使金鑰3和金鑰1相等,它不會像.NET Framework中會重用金鑰1中的位數。