3DES文件加密程序

參照<<密碼學引論>> 第二版 張煥國 王張宜編著這本書,用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
};
發佈了42 篇原創文章 · 獲贊 37 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章