參照<<密碼學引論>> 第二版 張煥國 王張宜編著這本書,用MFC編寫的框架,實現的使用3DES對文件進行加解密的程序
轉載請說明來源 : enjoy5512的博客 http://blog.csdn.net/enjoy5512
DES加密算法簡介
1977年1月,美國政府頒佈:採納IBM公司設計的方案作爲非機密數據的正式數據加密標準(DESData Encryption Standard) 。
目前在國內,隨着三金工程尤其是金卡工程的啓動,DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收費站等領域被廣泛應用,以此來實現關鍵數據的保密,如信用卡持卡人的PIN的加密傳輸,IC卡與POS間的雙向認證、金融交易數據包的MAC校驗等,均用到DES算法。
DES算法的入口參數有三個:Key、Data、Mode。
其中Key爲8個字節共64位,是DES算法的工作密鑰;
Data也爲8個字節64位,是要被加密或被解密的數據;
Mode爲DES的工作方式,有兩種:加密或解密。
DES算法是這樣工作的:
如Mode爲加密,則用Key 去把數據Data進行加密, 生成Data的密碼形式(64位)作爲DES的輸出結果;
如Mode爲解密,則用Key去把密碼形式的數據Data解密,還原爲Data的明碼形式(64位)作爲DES的輸出結果。
在通信網絡的兩端,雙方約定一致的Key,在通信的源點用Key對核心數據進行DES加密,然後以密碼形式在公共通信網(如電話網)中傳輸到通信網絡的終點,數據到達目的地後,用同樣的Key對密碼數據進行解密,便再現了明碼形式的核心數據。這樣,便保證了核心數據(如PIN、MAC等)在公共通信網中傳輸的安全性和可靠性。
通過定期在通信網絡的源端和目的端同時改用新的Key,便能更進一步提高數據的保密性,這正是現在金融交易網絡的流行做法。
3DES簡介
3DES是DES加密算法的一種模式,它使用3條64位的密鑰對數據進行三次加密。數據加密標準(DES)是美國的一種由來已久的加密標準,它使用對稱密鑰加密法。
3DES(即Triple DES)是DES向AES過渡的加密算法(1999年,NIST將3-DES指定爲過渡的加密標準),是DES的一個更安全的變形。它以DES爲基本模塊,通過組合分組方法設計出分組加密算法。
設Ek()和Dk()代表DES算法的加密和解密過程,K代表DES算法使用的密鑰,P代表明文,C代表密表,這樣,
3DES加密過程爲:C=Ek3(Dk2(Ek1(P)))
3DES解密過程爲:P=Dk1((EK2(Dk3(C)))
K1、K2、K3決定了算法的安全性,若三個密鑰互不相同,本質上就相當於用一個長爲168位的密鑰進行加密。多年來,它在對付強力攻擊時是比較安全的。若數據對安全性要求不那麼高,K1可以等於K3。在這種情況下,密鑰的有效長度爲112位
3DES加密流程圖
子密鑰產生
16輪加密
3DES加解密
代碼實現
(所有源碼在我的github上可以得到github.com/whu-enjoy/3DES)
因爲註釋挺詳細了,這裏就不細說
下面是我作的一個函數調用圖
其中對八字節的數據,調用EncryptBlock()三次
加密
解密
我的密鑰一跟密鑰三用的一樣的
加解密的類的實現
/////////////////////////////////////////////////////////////////////////////
// DES class used for Encrypt/Decrypt
class DES : public CDialog{
public:
//公共靜態成員數組
static int s_ia56PC_1[56]; //子密鑰產生算法中的PC1矩陣
static int s_ia16MoveTimes[16]; //子密鑰產生算法中的左移次數表
static int s_ia48PC_2[48]; //子密鑰產生算法中的PC2矩陣
static int s_ia64IP[64]; //加密算法中的初始置換IP矩陣
static int s_ia48E[48]; //加密算法中的擴展置換E矩陣
static int s_ia8_4_16S_Box[8][4][16]; //加密算法中的S盒
static int s_ia32P[32]; //加密算法中的P矩陣
static int s_ia64IP_1[64]; //加密算法中的逆初始置換IP^-1矩陣
int ia2_16_48K[2][16][48]; //子密鑰
public:
//程序說明開始
//==================================================================================
// 功能 : 將輸入的無符號字符數組轉化爲相應的二進制,轉換c_iCount個字節
// 參數 : const unsigned char c_ucaByte[], int iaBin[], const int c_iCount
// (入口) c_ucaByte : 需要轉換的無符號字符數組
// c_iCount : 需要轉換的字節數
// (出口) iaBin : 轉換後的二進制流,用整型數組保存結果
// 返回 : 無
// 主要思路 : 對每個字節循環八次,第j次循環左移j次,再將最高位提取出來,就能獲取這個
// 字節的八位上的所有數據
// 調用舉例 : unsigned char uca5Pwd[5] = "test";
// int ia32Bin[32];
// ByteToBin(uca5Pwd, ia32Bin, 4);
// 日期 : 2016年5月30日 19:17:10(註釋日期)
//==================================================================================
//程序說明結束
void ByteToBin(const unsigned char c_ucaByte[], int iaBin[], const int c_iCount)
{
int i = 0;
int j = 0;
//循環轉化c_iCount個字節
for ( i = 0; i < c_iCount; i++)
{
for(j = 0; j < 8; j++)
{
//第j次循環時,左移j次,再檢查最高位是否是1,如果是,則賦值爲1,否則賦值爲0
if (0x80 == ((c_ucaByte[i]<<j)&0x80))
{
iaBin[i*8+j] = 1;
}
else
{
iaBin[i*8+j] = 0;
}
}
}
}
//程序說明開始
//==================================================================================
// 功能 : 將輸入的二進制轉化爲相應的無符號字符數組,轉換c_iCount個字節
// 參數 : const int c_iaBin[], unsigned char ucaByte[], const int c_iCount
// (入口) c_iaBin : 需要轉換的二進制流
// c_iCount : 需要轉換的字節數
// (出口) ucaByte : 轉換後的無符號字符數組
// 返回 : 無
// 主要思路 : 對每個字節循環八次,第j次循環將原來的值左移一位,並將新的值加到最低位
// 調用舉例 : unsigned char uca5Pwd[5] = "";
// int ia32Bin[32] = {0,0,1,1,0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,0,1,1,0,0,1,1,0,1,0,0};
// BinToByte(ia32Bin, uca5Pwd, 4);
// 日期 : 2016年5月30日 19:24:25(註釋日期)
//==================================================================================
//程序說明結束
void BinToByte(const int c_iaBin[], unsigned char ucaByte[], const int c_iCount)
{
int i = 0;
int j = 0;
//轉換c_iCount個字節
for ( i = 0; i < c_iCount; i++)
{
for(j = 0; j < 8; j++)
{
//每次將原來的值左移一位,並加上新提取的
ucaByte[i] = ucaByte[i] * 2 + c_iaBin[i*8+j];
}
}
}
//程序說明開始
//==================================================================================
// 功能 : 根據輸入的第二個矩陣將第一個矩陣進行轉換,轉換的結果保存在第三個矩陣裏,
// 轉換c_iCount個數據
// 參數 : const int c_iaSource[], const int c_iaReplaceTable[]
// int iaReplaced[], const int c_iCount
// (入口) c_iaSource : 需要轉換的矩陣
// c_iaDisplaceTable : 轉換參考矩陣
// c_iCount : 需要轉換的數據個數
// (出口) iaReplaced : 轉換後的矩陣
// 返回 : 無
// 主要思路 : iaReplaced矩陣的第i個位置上的數是c_iaSource矩陣中
// 第c_iaReplaceTable[i]個位置的數據
// 調用舉例 : int ia64Source[64] = {xxxxx};
// int ia48Replace[48] = {xxxx};
// int ia48Replaced[48] = {0};
// Replacement(ia64Source, ia48Replace, ia48Replaced, 48);
// 日期 : 2016年5月30日 19:39:24(註釋日期)
//==================================================================================
//程序說明結束
void Replacement(const int c_iaSource[], const int c_iaReplaceTable[], int iaReplaced[], const int c_iCount)
{
int i = 0;
//循環c_iCount次
for (i = 0; i < c_iCount; i++)
{
//根據c_iaReplaceTable[]置換原表
iaReplaced[i] = c_iaSource[c_iaReplaceTable[i]-1];
}
}
//程序說明開始
//==================================================================================
// 功能 : 將輸入矩陣裏面的數據左移c_iCount次,用ia28Output保存左移後的結果
// 參數 : const int c_ia28Input[28], int ia28Output[28], const int c_iCount
// (入口) c_ia28Input : 需要左移的數組
// c_iCount : 需要左移的數據個數
// (出口) ia28Output : 左移後的數組
// 返回 : 無
// 主要思路 : 先將原數組前c_iCount個數據保存在局部變量i2Temp中,然後輸出數組的
// 前28-c_iCount數據左移c_iCount位,再將i2Temp中的數據接到輸出數組中
// 這樣做的時候,輸入數組盒輸出數組可以是同一個數組,減少內存空間使用量
// 調用舉例 : int ia28C[28] = {xxxxx};
// LeftMove(ia28C, ia28C, 2);
// 日期 : 2016年5月30日 19:49:46(註釋日期)
//==================================================================================
//程序說明結束
void LeftMove(const int c_ia28Input[28], int ia28Output[28], const int c_iCount)
{
int i2Temp[2] = {0}; //用於保存要移動的輸入數組前c_iCount個變量
int i = 0;
//保存前c_iCount個變量
for (i = 0; i < c_iCount; i++)
{
i2Temp[i] = c_ia28Input[i];
}
//將原數組後28-c_iCount個數前移c_iCount個位置
for (i = 0; i < 28-c_iCount; i++)
{
ia28Output[i] = c_ia28Input[i+c_iCount];
}
//將原數組前c_iCount個數接到輸出數組後
for (; i < 28; i++)
{
ia28Output[i] = i2Temp[c_iCount + i - 28];
}
}
//程序說明開始
//==================================================================================
// 功能 : 根據輸入的64位密鑰,輸出16組48位的子密鑰
// 參數 : const int c_ia64Pwd[64], int ia16_48K[16][48]
// (入口) c_ia64Pwd : 輸入64位密鑰
// (出口) ia16_64K : 16組48位子密鑰
// 返回 : 無
// 主要思路 : 先將64位密鑰通過PC1矩陣得到56位密鑰,再將密鑰分爲左右兩部分,根據左移表
// 循環左移16輪,每輪循環後合併成56位中間數據,然後在通過PC2矩陣得到48位子密鑰
// 調用舉例 : int ia64Pwd[64] = {xxxxx};
// int ia16_48K[16][48] = {0}
// SubKey(ia64Pwd, ia16_48K);
// 日期 : 2016年5月30日 19:58:32(註釋日期)
//==================================================================================
//程序說明結束
void SubKey(const int c_ia64Pwd[64], int ia16_48K[16][48])
{
int ia56Key[56] = {0}; //保存56位的密鑰
int ia28C[28] = {0}; //保存28位的密鑰左部分
int ia28D[28] = {0}; //保存28位的密鑰右部分
int i = 0;
int j = 0;
//先從給定的64位密鑰中通過PC_1矩陣獲取56位的密鑰
Replacement(c_ia64Pwd, s_ia56PC_1, ia56Key, 56);
//得到密鑰左右部分
for (i = 0; i < 28; i++)
{
ia28C[i] = ia56Key[i];
ia28D[i] = ia56Key[28+i];
}
//循環獲取16輪密鑰
for (i = 0; i < 16; i++)
{
//分別左移左右部分的密鑰
LeftMove(ia28C,ia28C,s_ia16MoveTimes[i]);
LeftMove(ia28D,ia28D,s_ia16MoveTimes[i]);
//密鑰合併
for (j = 0; j < 28; j++)
{
ia56Key[j] = ia28C[j];
ia56Key[28+j] = ia28D[j];
}
//通過置換選擇2矩陣獲取每一輪產生的密鑰
Replacement(ia56Key,s_ia48PC_2,ia16_48K[i],48);
}
}
//程序說明開始
//==================================================================================
// 功能 : 加密函數,在第i次迭代中用子密鑰Ki對Ri-1進行加密
// 參數 : const int c_ia32A[32], const int c_ia48K[48], int ia32Output[32]
// (入口) c_ia32A : 輸入32位數據
// c_ia48K : 48位子密鑰
// (出口) ia32Output : 32位加密結果
// 返回 : 無
// 主要思路 : 先用選擇運算矩陣E對輸入數據進行選擇和排列,輸出48位中間結果,這個結果和
// 48位子密鑰異或,然後送入S盒,得到32位輸出結果,這個結果再經過置換運算P,
// 將每一位打亂重排,得到加密結果
// 調用舉例 : int ia32Pwd[32] = {xxxxx};
// int ia48K[48] = {xxx}
// int ia32Output[32] = {0};
// F(ia32Pwd, ia48K, iaOutput);
// 日期 : 2016年5月30日 20:07:24(註釋日期)
//==================================================================================
//程序說明結束
void F(const int c_ia32A[32], const int c_ia48K[48], int ia32Output[32])
{
int ia48Temp[48] = {0}; //48位的中間數據
int iRow = 0; //S盒的行
int iCol = 0; //S盒的列
int i = 0;
//先將32位輸入通過選擇運算E擴展成48位
Replacement(c_ia32A, s_ia48E, ia48Temp, 48);
//48位中間結果與子密鑰異或
for (i = 0; i < 48; i++)
{
ia48Temp[i] = ia48Temp[i] ^ c_ia48K[i];
}
//循環從S盒中獲取32位的輸出結果
for (i = 0; i < 8; i++)
{
//獲取行列
iRow = ia48Temp[i*6]*2 + ia48Temp[i*6+5];
iCol = ia48Temp[i*6+1]*8 + ia48Temp[i*6+2]*4 + ia48Temp[i*6+3]*2 + ia48Temp[i*6+4];
//獲取S盒中的數據,並轉化成四位輸出
ia48Temp[i*4+0] = (s_ia8_4_16S_Box[i][iRow][iCol]&8)/8;
ia48Temp[i*4+1] = (s_ia8_4_16S_Box[i][iRow][iCol]&4)/4;
ia48Temp[i*4+2] = (s_ia8_4_16S_Box[i][iRow][iCol]&2)/2;
ia48Temp[i*4+3] = (s_ia8_4_16S_Box[i][iRow][iCol]&1);
}
//將S盒的32位輸出通過P矩陣獲取最終的32位輸出
Replacement(ia48Temp, s_ia32P, ia32Output, 32);
}
//程序說明開始
//==================================================================================
// 功能 : 對8字節的數據進行加解密,c_bFlag爲false代表加密操作,爲true代表解密操作
// 參數 : unsigned char uc9PlainText[9], const int c_ia16_48K[16][48], const bool c_bFlag
// (入口) c_ia_16_48K : 16輪子密鑰
// c_bFlag : 加解密標誌
// (出口) uc9Data : 32位加密結果
// 返回 : 無
// 主要思路 : 先將輸入的八字節數據轉化爲64位二進制,然後將64位二進制明文用初始置換
// 矩陣IP打亂,獲取左右半部分,然後進行16輪加密,每輪加密中,下一輪的左部分
// 是上一輪的右部分,然後用F函數實現密鑰K對上一輪右部分加密,得到的結果再
// 與上一輪的左部分異或,得到下一輪的右部分數據,這樣循環16次.第十六次迭代
// 結束後,將結果的左部分接到右部分上,得到64位的輸出,然後將64位數據經過逆
// 初始化矩陣IP^-1重新排列,得到最終密文
// 調用舉例 : unsigned char uc9Str = "12346578";
// int ia16_48K[16][48] = {xxx}
// EncryptBlock(uc9Str, ia16_48K, false); //加密
// EncryptBlock(uc9Str, ia16_48K, true); //解密
// 日期 : 2016年5月30日 20:20:41(註釋日期)
//==================================================================================
void EncryptBlock(unsigned char uc9Data[9], const int c_ia16_48K[16][48], const bool c_bFlag)
{
int ia64Data[64] = {0}; //加密的數據的64位二進制
int ia64Bin[64] = {0}; //臨時保存64位二進制數據
int ia32L_BK[32] = {0}; //每輪加密的左部分
int ia32R_BK[32] = {0}; //每輪加密的右部分
int ia32L[32] = {0}; //每輪加密結果的左部分
int ia32R[32] = {0}; //每輪加密結果的右部分
int i = 0;
int j = 0;
//將輸入的8字節轉換成64位二進制
ByteToBin(uc9Data, ia64Data, 8);
//將64位二進制明文通過初始置換矩陣IP打亂
Replacement(ia64Data,s_ia64IP,ia64Bin,64);
//獲取每輪加密的64位數據的左右部分
for (i = 0; i < 32; i++)
{
ia32L_BK[i] = ia64Bin[i];
ia32R_BK[i] = ia64Bin[32+i];
}
//16輪加密
for (i = 0; i < 16; i++)
{
//獲取每輪加密後的左部分
for (j = 0; j < 32; j++)
{
ia32L[j] = ia32R_BK[j];
}
//如果c_bFlag爲false,則加密,爲true則解密
if (0 == c_bFlag)
{
F(ia32R_BK,c_ia16_48K[i],ia32R); //加密
}
else
{
F(ia32R_BK,c_ia16_48K[15-i],ia32R); //解密
}
//獲取每輪加密後的右部分
for (j = 0; j < 32; j++)
{
ia32R[j] = ia32R[j] ^ ia32L_BK[j];
}
//保存當前左右部分的數據
for (j = 0; j < 32; j++)
{
ia32L_BK[j] = ia32L[j];
ia32R_BK[j] = ia32R[j];
}
}
//16輪加密後,把加密結果的左部分接到右部分後面
for (i = 0; i < 32; i++)
{
ia64Bin[i] = ia32R[i];
ia64Bin[i+32] = ia32L[i];
}
//通過逆初始化矩陣,獲取加密後的64位數據
Replacement(ia64Bin, s_ia64IP_1, ia64Data, 64);
//將64位數據轉化爲8位輸出
BinToByte(ia64Data, uc9Data, 8);
}
//程序說明開始
//==================================================================================
// 功能 : 對文件進行加密
// 參數 : const CString c_csOpenFilePath, const CString c_csSaveFilePath
// const unsigned char c_uca9Pwd1[9], const unsigned char c_uca9Pwd2[9]
// (入口) c_csOpenFilePath : 要加密的文件路徑
// c_csSaveFilePath : 加密後的文件保存路徑
// c_uca9Pwd1 : 密鑰一
// c_uca9Pwd2 : 密鑰二
// (出口) 無
// 返回 : true代表加密成功, false代表打開加密文件或創建保存加密結果的文件失敗
// 主要思路 : 1.先打開文件指針
// 2.根據輸入的兩個密鑰,獲取兩組16*48位的子密鑰
// 3.獲取加密文件長度,寫入保存文件的前八字節
// 4.獲取加密文件類型,寫入保存文件的9-16字節
// 5.循環讀取加密文件,每次讀取八字節,文件尾不夠的用0填充到8字節.
// 在每輪循環裏,先用子密鑰一對數據加密,然後用密鑰二對數據解密
// 然後再用密鑰一對數據加密,之後將加密後的數據寫入文件
// 調用舉例 : CString csOpenFilePath = L"c:\test.txt";
// CString csSaveFilePath = L"c:\test.ept":
// unsigned char uca9Pwd1[9] = "12345678";
// unsigned char uca9Pwd2[9] = "98756412";
// DES CcmDes;
// CcmDes.FileEncrypt(csOpenFilePath, csSaveFilePath, uca9Pwd1, uca9Pwd2);
// 日期 : 2016年5月30日 20:36:32(註釋日期)
//==================================================================================
bool FileEncrypt(const CString c_csOpenFilePath, const CString c_csSaveFilePath, const unsigned char c_uca9Pwd1[9], const unsigned char c_uca9Pwd2[9])
{
CString csFileExt = L""; //保存加密後的文件的後綴名
unsigned char uc9PlainText[9] = {0}; //保存8字節的數據
int ia64Bin[64] = {0}; //保存64位中間結果
bool bStatus = true; //設置返回狀態爲true
FILE *pOpenFile = NULL; //加密文件指針
FILE *pSaveFile = NULL; //加密結果文件指針
int iCharNum = 0; //讀取的字節數
int i = 0;
int j = 0;
int iFileLen = 0; //加密文件長度
//打開加密文件指針
if (NULL == (pOpenFile = fopen(c_csOpenFilePath,"rb")))
{
MessageBox("打開解密文件失敗!");
return false;
}
//打開加密後的文件指針
if (NULL == (pSaveFile = fopen(c_csSaveFilePath,"wb")))
{
MessageBox("創建文件失敗!");
return false;
}
//將8字節密鑰一轉化爲64位二進制
ByteToBin(c_uca9Pwd1, ia64Bin, 8);
//獲取16輪子密鑰一
SubKey(ia64Bin,ia2_16_48K[0]);
//將8字節密鑰二轉化爲64位二進制
ByteToBin(c_uca9Pwd2, ia64Bin, 8);
//獲取16輪子密鑰2
SubKey(ia64Bin,ia2_16_48K[1]);
//將文件指針移動到文件尾
fseek(pOpenFile, 0, SEEK_END);
//獲取文件長度
iFileLen = ftell(pOpenFile);
//將文件指針設置迴文件頭
fseek(pOpenFile, 0, SEEK_SET);
//將文件長度保存,爲了滿足八字節,所以寫了兩次
fwrite(&iFileLen,4,1,pSaveFile);
fwrite(&iFileLen,4,1,pSaveFile);
//獲取加密文件的後綴名
csFileExt = c_csOpenFilePath.Mid(c_csOpenFilePath.ReverseFind('.')+1);
//將加密文件後綴名寫入加密後的文件,以便於解密時恢復源文件
fwrite(csFileExt,1,8,pSaveFile);
//設置讀取的字節數爲0
iCharNum = 0;
//循環讀取加密文件,直到文件尾
while(0 != (iCharNum = fread(uc9PlainText,1,8,pOpenFile)))
{
//如果讀取到的不是8個字節,說明到文件尾了,填充0到八字節長
if (8 != iCharNum)
{
for (i = iCharNum; i < 8; i++)
{
uc9PlainText[i] = 0;
}
}
//用密鑰一加密
EncryptBlock(uc9PlainText, ia2_16_48K[0], false);
//用密鑰二解密
EncryptBlock(uc9PlainText, ia2_16_48K[1], true);
//再用密鑰一加密
EncryptBlock(uc9PlainText, ia2_16_48K[0], false);
//將加密結果寫入文件
fwrite(uc9PlainText, 1, 8, pSaveFile);
}
//關閉文件指針,返回
fclose(pOpenFile);
fclose(pSaveFile);
return bStatus;
}
//程序說明開始
//==================================================================================
// 功能 : 對文件進行解密
// 參數 : const CString c_csOpenFilePath, CString csSaveFilePath
// const unsigned char c_uca9Pwd1[9], const unsigned char c_uca9Pwd2[9]
// (入口) c_csOpenFilePath : 要解密的文件路徑
// c_uca9Pwd1 : 密鑰一
// c_uca9Pwd2 : 密鑰二
// (出口) csSaveFilePath : 解密後的文件保存路徑
// 返回 : true代表解密成功, false代表打開解密文件或創建保存解密結果的文件失敗
// 主要思路 : 1.先打開解密文件指針
// 2.讀取解密文件前4字節獲取解密後的文件大小
// 3.讀取解密文件8-16字節獲取解密後的文件類型
// 4.創建解密後的文件
// 5.根據輸入的兩個密鑰,獲取兩組16*48位的子密鑰
// 8.循環讀取解密文件,每次讀取八字節,已解密的數據長度加8
// 在每輪循環裏,先用子密鑰一對數據解密,然後用密鑰二對數據加密
// 然後再用密鑰一對數據解密
// 如果已解密的數據長度小於文件長,則寫入後繼續解密
// 否則,寫入數據到原文件長度(原文件並不一定是8字節對齊
// 但是加密後的文件卻填充到8字節對齊狀態),並退出解密
// 調用舉例 : CString csOpenFilePath = L"c:\test.ept";
// CString csSaveFilePath = L"c:\test.":
// unsigned char uca9Pwd1[9] = "12345678";
// unsigned char uca9Pwd2[9] = "98756412";
// DES CcmDes;
// CcmDes.FileDecrypt(csOpenFilePath, csSaveFilePath, uca9Pwd1, uca9Pwd2);
// 日期 : 2016年5月30日 20:49:46(註釋日期)
//==================================================================================
bool FileDecrypt(const CString c_csOpenFilePath, CString csSaveFilePath, const unsigned char c_uca9Pwd1[9], const unsigned char c_uca9Pwd2[9])
{
char ca8FileExt[8] = {0}; //保存解密後的文件的後綴名
unsigned char uc9PlainText[9] = {0}; //保存8字節數據
int ia64PlainText[64] = {0}; //保存64位數據
int ia64Bin[64] = {0}; //保存64爲中間數據
bool bStatus = true; //設置返回狀態爲true
FILE *pOpenFile = NULL; //要解密的文件指針
FILE *pSaveFile = NULL; //解密後的文件指針
int iFileLen = 0; //文件長度
int iDecryptedCharNum = 0; //已解密的數據長度
int i = 0;
int j = 0;
//打開要解密的文件指針
if (NULL == (pOpenFile = fopen(c_csOpenFilePath,"rb")))
{
MessageBox("打開解密文件失敗!");
return false;
}
//讀取要解密的文件頭四個字節,獲取解密後的文件的長度
//讀取兩遍是爲了8字節對齊
fread(&iFileLen,4,1,pOpenFile);
fread(&iFileLen,4,1,pOpenFile);
//讀取要解密的文件的9-16字節數據,獲取解密後的文件後綴名
fread(ca8FileExt,1,8,pOpenFile);
//將後綴名連接到解密後的文件路徑中
csSaveFilePath = csSaveFilePath + ca8FileExt;
//打開解密後的文件指針
if (NULL == (pSaveFile = fopen(csSaveFilePath,"wb")))
{
MessageBox("創建文件失敗!");
return false;
}
//將密鑰一轉化爲64位二進制
ByteToBin(c_uca9Pwd1, ia64Bin, 8);
//獲取16輪子密鑰一
SubKey(ia64Bin, ia2_16_48K[0]);
//將密鑰二轉化爲64位二進制
ByteToBin(c_uca9Pwd2, ia64Bin, 8);
//獲取16輪子密鑰二
SubKey(ia64Bin, ia2_16_48K[1]);
//設置已解密的數據長度爲0
iDecryptedCharNum = 0;
//循環解密
while(1)
{
//讀取8字節密文
fread(uc9PlainText,1,8,pOpenFile);
//已解密數據長度加8
iDecryptedCharNum += 8;
//用子密鑰一解密
EncryptBlock(uc9PlainText, ia2_16_48K[0], true);
//用子密鑰二加密
EncryptBlock(uc9PlainText, ia2_16_48K[1], false);
//用子密鑰一解密
EncryptBlock(uc9PlainText, ia2_16_48K[0], true);
//如果已解密的數據長度小於文件長,則寫入後繼續解密
//否則,寫入數據到原文件長度(原文件並不一定是8字節對齊
//但是加密後的文件卻填充到8字節對齊狀態),並退出解密
if (iDecryptedCharNum < iFileLen)
{
fwrite(uc9PlainText, 1, 8, pSaveFile);
}
else
{
fwrite(uc9PlainText, 1, 8 + iFileLen - iDecryptedCharNum, pSaveFile);
break;
}
}
//關閉文件指針,並退出
fclose(pOpenFile);
fclose(pSaveFile);
return bStatus;
}
};
//子密鑰產生算法中的置換選擇2矩陣
int DES::s_ia56PC_1[56] =
{
57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4
};
//子密鑰產生算法中左移位數表
int DES::s_ia16MoveTimes[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
//子密鑰產生算法中的置換選擇2矩陣
int DES::s_ia48PC_2[48] =
{
14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32
};
//初始置換IP矩陣
int DES::s_ia64IP[64] =
{
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7
};
//擴展置換E矩陣
int DES::s_ia48E[48] =
{
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1
};
//S盒
int DES::s_ia8_4_16S_Box[8][4][16] =
{
//S1
{{14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7},
{0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},
{4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},
{15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}},
//S2
{{15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10},
{3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5},
{0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15},
{13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9}},
//S3
{{10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8},
{13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1},
{13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7},
{1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12}},
//S4
{{7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15},
{13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9},
{10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4},
{3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14}},
//S5
{{2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9},
{14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6},
{4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14},
{11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3}},
//S6
{{12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11},
{10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8},
{9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6},
{4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13}},
//S7
{{4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1},
{13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6},
{1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2},
{6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12}},
//S8
{{13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7},
{1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2},
{7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8},
{2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11}}
};
//置換運算P矩陣
int DES::s_ia32P[32] =
{
16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25
};
//逆初始置換IP^-1
int DES::s_ia64IP_1[64] =
{
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25
};