密码学与网络安全(四)DES加密过程与C++全独立实现

Oct 9, 2017


原创:岐山凤鸣

引用需注明本站域名。

总述

先直接看一遍加密性质:

  • 分组密码:明文密文都是64位
  • 字符都变成二进制,即每一位都是一个二进制,0或者1
  • 密钥长度56位(64-8),生命周期短,运算速度慢

然后是加密的过程,分为两步,第一步是密钥生成,第二步才是加密。

总概述过程如下:

  • 密钥生成
    • Step1:输入64位密钥,各分为8个8字节的段,然后去掉每个8字节的第八位(奇偶校验位),再合起来形成56位
    • Step2:将这个56位按照下表打乱顺序,表的顺序是先行后列,表中的数字指的是原来的那个位的数字。 image
    • Step3:然后将这个56位中间分段,分成两个28位,前28位叫做C0,后28位叫做D0
    • Step4:C0和D0合起来的56位,即打乱顺序后的那个56位是我们得到的第一个子密钥,叫做K0,我们一共需要16个子密钥,即K0-k15,下面就是要生成这剩下的15个子密钥
    • Step5:将C0和D0按照下面的表进行循环左移,因为我们现在是要得到C1,所以只需要看表的第一列,即1,所以两个都只需循环左移1位,得到C1和D1 image
    • Step6:将C1和D1合并,形成新的56位,然后按照下表打乱顺序,得到K1 image
    • Step7: 重复Step5和Step6,15次,得到K1到K15,结束
  • 加密
    • Step1:输入64位数据块,进行初始置换,输出32位R0和L0
    • Step2:将L0和R0进行16次f函数运算,每一次要用到相应的密钥K_i(f函数在下面会详细说道),输出最后的32位R0和32位L0
    • Step3:将最后的R0和L0进行逆置换,输出64位密文,结束,总过程如下图 image

加密过程详细

密钥生成不再叙述,这里从头详细说一遍加密过程

开始之前:我们有64位明文数据块,16个子密钥K0-K15

Step1、初始置换

将64位数据块按照下面的表打乱顺序

image

打乱后从中切开,左32位提出叫做L0,后32位提出叫做R0,【只输入R0】进入下个步骤

Step2、f函数

f函数分为三个步骤

第一步、扩展32位R0数据块

按照下面的表进行扩展,将32位R0扩展成48位

32 01 02 03 04 05
04 05 06 07 08 09
08 09 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 01

第二步、与子密钥模二相加

将这个48位和对应的子密钥(第一次运算f函数就是K0,第十六次就是K15)模二相加,得到新的48位。

第三步、S盒替代

将这个新的48位按照每6位分成8个块,每个块是6位。

对每个块来说,将第一位和第六位合在一起的两位,计算出一个0-3的数字,叫做行,再将剩余的四位合在一起计算出一个0-15的数字,叫做列,一共得到8对行和列,然后在下面8个表S1-S8中,分别按照行列,找出8个十进制数字。这个过程叫做S盒替代。

image

将得到的8个十进制数字全部转成四位二进制合成,得到32位。

第四步、P盒置换

将这个32位再按照下面的表打乱顺序

image

第五步、得到R1

这样得到了最终的32位,现在,我们将这个32位叫做新的R0,即R1,将R1与之前一直没有改动的L0模二相加,得到新的R1。

第六步、得到新的L1和R1,回到第一步,重复f函数

将之前的没有变动的32位R0改成L1,现在我们有了新的L1和R1,可以重复这六步,得到L2和R2,重复16次,得到L15和R15

Step3、合并L15和R15,逆置换

合并L15和R15,得到64位数据,按照下表进行逆置换打乱顺序

image

结束,得到64位密文

代码实现

类的设计:

#ifndef DESENCRYPTOR_H
#define DESENCRYPTOR_H
#include <iostream>
using namespace std;

class DESEncryptor
{
public:
    DESEncryptor(){keys = new string[16];}
    ~DESEncryptor(){}
    string* keys;                                       // 包含16个48位子密钥
    string encrypt(string sourceText, string *keys);    // 加密算法,输入64位数据块,和16个子密钥
    void getKey(string sourceKey);                      // 得到16个48位子密钥,输入64位原始密钥
//private:
    int initialPermutationTable[8][8] = {
        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
    };                                                  // 初始置换矩阵
    int inverseInitialPermutationTable[8][8] = {
        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
    };                                                  // 逆置换矩阵
    int expandTable[8][6] = {
        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, 01
    };                                                  // 32位扩展到48位矩阵
    int s1[4][16] = {
        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
    };                                                  // S盒的第一个矩阵,下面7个都是S盒替代的
    int s2[4][16] = {
        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
    };
    int s3[4][16] = {
        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
    };
    int s4[4][16] = {
        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
    };
    int s5[4][16] = {
        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,
    };
    int s6[4][16] = {
        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
    };
    int s7[4][16] = {
        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
    };
    int s8[4][16] = {
        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
    };
    int pTable[4][8] = {
        16,  7, 20, 21, 29, 12, 28, 17,
         1, 15, 23, 26,  5, 18, 31, 10,
         2,  8, 24, 14, 32, 27,  3,  9,
         9, 13, 30,  6, 22, 11,  4, 25
    };                                                    // P置换矩阵
    int C0D0[8][7] = {
        57, 49, 41, 33, 25, 17, 9,
        10, 2, 59, 51, 43, 35, 27,
        63, 55, 47, 39, 31, 23, 15,
        14, 6, 61, 53, 45, 37, 29,
        1, 58, 50, 42, 34, 26, 18,
        19, 11, 3, 60, 52, 44, 36,
        7, 62, 54, 46, 38, 30, 22,
        21, 13, 5, 28, 20, 12, 4
    };                                                    // C0置换矩阵,D0置换矩阵
    int iterTimes[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
                                                          // 循环左移次数
    int keyPermutationTable[4][12] = {
        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
    };                                                    // 密钥置换
    string initialPermutation(string);                    // 初始置换
    string inverseInitialPermutation(string);             // 逆置换
    string pPermutaion(string);                           // P置换,输入32位数据,输出32位数据
    string expansionPermutation(string);                  // 扩展置换,输入32位数据,输出48位扩展数据
    string c0d0Permutation(string);                       // c0d0置换,输入56位,输出56位
    string keyPermutation(string);                        // 密钥置换,输入56位,输出48位子密钥
    string sBox(string);                                  // s盒置换,输入了48位数据,经过8个S盒的替代后,输出32位数据


    string feistelCipher(string, string);                 // 一次f函数,包含了上述的各种步骤,运用了本类中的各种函数

    string str2bit(string sourceText);                    // 字符串到二进制,输入字符串,输出其二进制串
    string bit2str(string bitText);                       // 二进制到字符串,输入二进制串,输出字符串
    string num2bit(int num, int bit);                     // 数字到二进制,只支持0-15的数字,输入数字字符串,和2位还是4位,输出转换后的2位或者4位数据
    string bit2num(string bit);                           // 二进制到数字,上面的翻转
    string myXor(string str1, string str2);               // 模二相加,输入的两个串必须大小相同
    string leftShift(string);                             // 循环左移,输入一个二进制串和左移位数
};

#endif // DESENCRYPTOR_H

具体设计细节待定,cpp代码:

#include "desencryptor.h"
#include <cmath>
#include <exception>
#include <map>
#include <sstream>

string DESEncryptor::str2bit(string sourceText){
    string ans;
    for(string::iterator it = sourceText.begin(); it != sourceText.end(); ++it){
        unsigned char k = 0x80;
        for(int i = 0; i < 8; ++i, k >>= 1){
            if((*it) & k){
                ans = ans + "1";
            }else{
                ans = ans + "0";
            }
        }
    }
    return ans;
}

string DESEncryptor::bit2str(string bitText){
    string ans;
    int times = (bitText.size() / 8);
    for(int i = 0; i < times; ++i){
        string tmp = bitText.substr(i * 8, 8);
        unsigned char k = 0;
        for(int j = 0; j < 8; ++j){
            k = k + (tmp.at(j) - '0') * (int)(pow(2, 7 - j));
        }
        stringstream stream;
        stream<<k;
        ans = ans + stream.str();
    }
    return ans;
}

string DESEncryptor::initialPermutation(string sourceText){
    string ans = sourceText + "";
    for(int i = 0; i < 8; ++i){
        for(int j = 0; j < 8; ++j){
            ans[8 * i + j] = sourceText[initialPermutationTable[i][j] - 1];
        }
    }
    return ans;
}

string DESEncryptor::inverseInitialPermutation(string sourceText){
    string ans = sourceText + "";
    for(int i = 0; i < 8; ++i){
        for(int j = 0; j < 8; ++j){
            ans[8 * i + j] = sourceText[inverseInitialPermutationTable[i][j] - 1];
        }
    }
    return ans;
}

string DESEncryptor::pPermutaion(string sourceText){
    string ans = sourceText + "";
    for(int i = 0; i < 4; ++i){
        for(int j = 0; j < 8; ++j){
            ans[8 * i + j] = sourceText[pTable[i][j] - 1];
        }
    }
    return ans;
}

string DESEncryptor::expansionPermutation(string sourceText){
    string ans;
    for(int i = 0; i < 48; ++i)
        ans = ans + "1";

    for(int i = 0; i < 8; ++i){
        for(int j = 0; j < 6; ++j){
            ans[6 * i + j] = sourceText[expandTable[i][j] - 1];
        }
    }
    return ans;

}

string DESEncryptor::myXor(string str1, string str2){
    if(str1.size() != str2.size()){
        cout<<"***Error when myXor"<<endl;
        return "";
    }
    string ans = str1 + "";
    for(int i = 0; i < str1.size(); ++i){
        if(str1.at(i) == str2.at(i)){
            ans[i] = '0';
        }else{
            ans[i] = '1';
        }
    }
    return ans;
}

string DESEncryptor::feistelCipher(string sourceText, string key){
    string expand = expansionPermutation(sourceText);
    expand = myXor(expand, key);
    string afterSBox = sBox(expand);
    string afterPBox = pPermutaion(afterSBox);
    return afterPBox;
}

string DESEncryptor::num2bit(int num, int bit){
    if(bit == 2){
        map<int, string> table;
        table[0] = "00";
        table[1] = "01";
        table[2] = "10";
        table[3] = "11";
        try{
            return table[num];
        }catch (exception& e){
            cout<<"***num2bit Error"<<endl;
        }
    }else if(bit == 4){
        map<int, string> table;
        table[0] = "0000";
        table[1] = "0001";
        table[2] = "0010";
        table[3] = "0011";
        table[4] = "0100";
        table[5] = "0101";
        table[6] = "0110";
        table[7] = "0111";
        table[8] = "1000";
        table[9] = "1001";
        table[10]= "1010";
        table[11]= "1011";
        table[12]= "1100";
        table[13]= "1101";
        table[14]= "1110";
        table[15]= "1111";
        try{
            return table[num];
        }catch (exception& e){
            cout<<"***num2bit Error"<<endl;
        }
    }else{
        cout<<"***num2bit Error, expected int bit"<<endl;
    }
}

string DESEncryptor::bit2num(string bit){
    map<string, string> table;
    table["0000"] = "0";
    table["0001"] = "1";
    table["0010"] = "2";
    table["0011"] = "3";
    table["0100"] = "4";
    table["0101"] = "5";
    table["0110"] = "6";
    table["0111"] = "7";
    table["1000"] = "8";
    table["1001"] = "9";
    table["1010"]= "10";
    table["1011"]= "11";
    table["1100"]= "12";
    table["1101"]= "13";
    table["1110"]= "14";
    table["1111"]= "15";
    table["00"] = "0";
    table["01"] = "1";
    table["10"] = "2";
    table["11"] = "3";
    table["0"] = "0";
    table["1"] = "1";
    try{
        return table[bit];
    }catch (exception& e){
        cout<<"***bit2num Error"<<endl;
    }
}

string DESEncryptor::sBox(string sourceText){
    // input 48bits, output 32bits
    string ans;
    // divided into 8 parts

    // sourceText[0 - 5]
    int row = stoi(bit2num(sourceText.substr(0, 1) + sourceText.substr(5, 1)));
    int col = stoi(bit2num(sourceText.substr(1, 4)));
    ans = ans + num2bit(s1[row][col], 4);
    // sourceText[6 - 11]
    row = stoi(bit2num(sourceText.substr(6, 1) + sourceText.substr(11, 1)));
    col = stoi(bit2num(sourceText.substr(7, 4)));
    ans = ans + num2bit(s2[row][col], 4);
    // sourceText[12 - 17]
    row = stoi(bit2num(sourceText.substr(12, 1) + sourceText.substr(17, 1)));
    col = stoi(bit2num(sourceText.substr(13, 4)));
    ans = ans + num2bit(s3[row][col], 4);
    // sourceText[18 - 23]
    row = stoi(bit2num(sourceText.substr(18, 1) + sourceText.substr(23, 1)));
    col = stoi(bit2num(sourceText.substr(19, 4)));
    ans = ans + num2bit(s4[row][col], 4);
    // sourceText[24 - 29]
    row = stoi(bit2num(sourceText.substr(24, 1) + sourceText.substr(29, 1)));
    col = stoi(bit2num(sourceText.substr(25, 4)));
    ans = ans + num2bit(s5[row][col], 4);
    // sourceText[30 - 35]
    row = stoi(bit2num(sourceText.substr(30, 1) + sourceText.substr(35, 1)));
    col = stoi(bit2num(sourceText.substr(31, 4)));
    ans = ans + num2bit(s6[row][col], 4);
    // sourceText[36 - 41]
    row = stoi(bit2num(sourceText.substr(36, 1) + sourceText.substr(41, 1)));
    col = stoi(bit2num(sourceText.substr(37, 4)));
    ans = ans + num2bit(s7[row][col], 4);
    // sourceText[42 - 47]
    row = stoi(bit2num(sourceText.substr(42, 1) + sourceText.substr(47, 1)));
    col = stoi(bit2num(sourceText.substr(43, 4)));
    ans = ans + num2bit(s8[row][col], 4);
    return ans;
}

string DESEncryptor::encrypt(string sourceText, string *key){
    sourceText = str2bit(sourceText);
    sourceText = initialPermutation(sourceText);
    string L = sourceText.substr(0, 32);
    string R = sourceText.substr(32, 32);
    for(int i = 0; i < 16; ++i){
        R = feistelCipher(R, key[i]);
        R = myXor(L, R);
        string tmp = L;
        L = R;
        R = tmp;
    }
    sourceText = L + R;
    return inverseInitialPermutation(sourceText);
}

string DESEncryptor::c0d0Permutation(string sourceText){
    string ans;
    for(int i = 0; i < 8; ++i){
        for(int j = 0; j < 7; ++j){
            ans = ans + sourceText[C0D0[i][j] - 1];
        }
    }
    return ans;
}

string DESEncryptor::keyPermutation(string sourceKey){
    string ans;
    for(int i = 0; i < 48; ++i)
        ans = ans + "0";
    for(int i = 0; i < 4; ++i){
        for(int j = 0; j < 12; ++j){
            ans[12 * i + j] = sourceKey[keyPermutationTable[i][j] - 1];
        }
    }
    return ans;
}

void DESEncryptor::getKey(string sourceKey){
    string sourceText = c0d0Permutation(sourceKey);
    keys[0] = keyPermutation(sourceText);
    string c0 = sourceText.substr(0, 28);
    string d0 = sourceText.substr(28, 28);
    for(int i = 0; i < 15; ++i){
        if(iterTimes[i + 1] == 1){
            c0 = leftShift(c0);
            d0 = leftShift(d0);
        }else if(iterTimes[i + 1] == 2){
            c0 = leftShift(c0);
            d0 = leftShift(d0);
            c0 = leftShift(c0);
            d0 = leftShift(d0);
        }
        keys[i + 1] = keyPermutation(c0 + d0);
    }
}

string DESEncryptor::leftShift(string sourceText){
    string ans = sourceText + "";
    for(int j = 0; j < sourceText.size() - 1; ++j){
        ans[j] = sourceText[j + 1];
    }
    ans[sourceText.size() - 1] = sourceText[0];
    return ans;
}