目录

  • 一、实验内容
  • 二、实验原理
  • 2.1 DES加解密原理
  • 2.1.1 DES加解密的基本原理
  • 2.1.2 DES加解密的关键步骤
  • 2.2 3DES加解密原理
  • 2.3 分组密码CBC加解密模式原理
  • 2.4 填充原理
  • 三、实验过程
  • 3.1 变量说明
  • 3.1.1 主函数变量说明
  • 3.1.2 其他重要变量说明
  • 3.2 函数功能说明
  • 3.2.1主函数说明
  • 3.2.2 其他重要函数说明
  • 3.3 流程图
  • 3.3.1 主函数流程图
  • 3.3.2 其他重要函数流程图
  • 3.3.2.1 part_F函数流程图
  • 3.3.2.2 generateKeys函数流程图
  • 3.3.2.3 encryptDES函数流程图
  • 四、实验结果与截图
  • 五、实验总结
  • 六、代码


一、实验内容

(1)编程实现DES加解密算法,并使用DES加解密算法实现3DES加解密算法
(2)选择一种填充方式,对需要加密的文件进行填充(解密要去掉填充部分)
(3)DES的加解密的工作模式,采用密码分组链接(CBC)模式
(4)读取/写入被加密/解密文件时,采用字节流的形式进行文件读取/写入

二、实验原理

2.1 DES加解密原理

2.1.1 DES加解密的基本原理

  DES的加密过程具体如图1所示。

des cbc在线加解密 des加解密过程_信息安全

图1的加密过程具体可以分为以下几个步骤
1)输入64位明文进行IP置换,分成两个32位分支,左边L0,右边R0
2)右分支:L1 = R0 左分支:引入48位的密钥,R1 = L0 ⊕ F(R0,K1)
3)相同的操作进行16次的运算循环,算出相应的,R1 ~ R16,L0 ~ L16
4)最后在进行IP的逆序置换,将左右两个分支再次合并为64位密文
解密和加密过程是类似的,唯一不同的是秘钥的次序是相反的。如果每一轮的加密秘钥分别是K1、K2、K3…K16,那么解密秘钥就是K16、K15、K14…K1。为每一轮产生秘钥的算法也是循环的。加密是秘钥循环左移,解密是秘钥循环右移。

2.1.2 DES加解密的关键步骤

  通过图1可知DES算法分为两大部分——迭代加密和子密钥生成。

子密钥生成的过程具体如图2所示。

des cbc在线加解密 des加解密过程_des cbc在线加解密_02


  图2中的子密钥生成过程可以分为以下几个步骤

1)将64位主钥通过置换选择1变为56 位密钥,它分成左右两个28位半密钥

2)将左、右两个半密钥循环左移指定次数

3)将左右半密钥拼接成56位密钥,再进行置换选择2 ,生成48位的子密钥

4)重复步骤 2、步骤 3 一共 16 次,于是得到了 16 个 48位的子密钥

  迭代加密是进行了16轮完全相同的运算,在运算过程中数据与扩展后秘钥结合。函数F(如图3所示)的输出经过一个异或运算,和左半部分结合形成新的右半部分,原来的右半部分成为新的左半部分。每轮迭代的过程用如下公式表示
Ln = R(n - 1);
Rn = L(n - 1) ⊕ F(Rn-1,kn-1)

des cbc在线加解密 des加解密过程_CBC模式_03


  图3中的轮函数F的具体流程,将一个 32位的块,经过扩展置换变成 48 位,然后与子密钥异或。得到的 48位的结果分为 8 组,每一组是 6位,进行 S 盒替换,输出 4位。把替换结果拼接成32位,进行P 盒置换,得到 32位的结果。其中32位的块与子密钥的混合过程,以及 S 盒提供的强有力的混淆能力,提供了 DES 体系的核心安全性。

2.2 3DES加解密原理

  3DES,该算法的加解密过程分别是对明文/密文数据进行三次DES加密或解密,得到相应的密文或明文。
假设E()和D()分别表示DES的加密和解密函数,M表示明文,C表示密文,那么加解密的公式如下:
加密:C=E(D(E(M,K1),K2),K3),即对明文数据进行加密 --> 解密 --> 加密的过程,最后得到密文数据。
解密:M=D(E(D(C,K3),K2),K1),即对密文数据进行解密 --> 加密 --> 解密的过程,最后得到明文数据。

2.3 分组密码CBC加解密模式原理

  CBC加密过程如图4所示可以分为以下几个步骤
1)首先将明文数据按照8个字节一组进行分组得到M1,M2,…Mn(若数据不是8的整数倍,运用一定的填充原理进行填充)
2)明文M1与初始化向量IV异或后的结果进行DES加密得到密文C1
3)明文M2与密文C1异或后的结果进行DES加密,得到密文C2
4)之后的数据以此类推,得到Cn,将C1,C2,C3…Cn拼接即得到密文

des cbc在线加解密 des加解密过程_课程设计_04

  CBC解密过程是加密过程的逆过程,如图5所示可以分为以下几个步骤
1)首先将密文按照8个字节一组进行分组得到C1,C2,C3…Cn
2)将密文C1进行解密后与初始化向量IV进行异或得到明文M1
3)将密文C2进行解密后与密文C1进行异或得到明文M2
4)之后依此类推得到Mn,将M1,M2,M3…Mn解填充,拼接得到明文

des cbc在线加解密 des加解密过程_3DES加解密_05

2.4 填充原理

  此次加解密的数据是以字节流的形式,64位数据用十六进制表示为16位。
  加密时将明文分为8字节(2位)一组,对明文尾部分类讨论进行填充。
1)最后一个分组不足8字节,则填充为00 00…0X(最后一个字节保存包括最后一个字节在内的所填充的字节数量)
2)最后一个分组正好是8个字节,则填充为00 00…08

  解密时先将密文解密为明文,再对明文的最后一个8字节分组进行解填充。
1)最后一个分组正好是0000…08,则去掉该分组
2)最后一个分组满足最后一个字节是0X,且0X前面有x-1对0,则去掉该组数据分组中的0X和X-1对0

三、实验过程

3.1 变量说明

3.1.1 主函数变量说明

String in_filename; //读取文件的文件路径名
String out_filename; //写入文件的文件路径名
Int model; //用户手动输入的模式值(1 / 2),指定进行加密还是解密
Int fillsize; //记录填充需要的位数
Bitset <64> initV; //保存初始化向量
Bitset <64> pretext; // 保存上一次加密得到的密文,用以CBC模式中的异或
Bitset <64> res; // 保存CBC函数返回的结果,写入文件中的数据

3.1.2 其他重要变量说明

Int initPer[]; //初始置换IP表
Int reverseInitPer[]; //逆初始置换IP-1表
Int permuChoice_1[]; //置换选择1表 64位 -> 56位
Int permuChoice_2[]; //置换选择2表 56位 -> 48位
Int leftShiftbit[16]; //左循环移位表,除了1,2,9,16移动一位,其余都是2
Int extendPer[]; //扩展至换E表 32位 -> 48位
Int sBox[8][4][16]; //S盒
Int pBox[]; //P盒
Bitset <48> keys[16]; //保存生成的子密钥
bitset <64> C, M; //加解密中存储的密文和明文
bitset <64> K1, K2, K3; 3DES加解密中需要的密钥

3.2 函数功能说明

3.2.1主函数说明

1)从文件中读取数据,将加解密的结果写入文件中
2)输出提示语句,实现与用户的交互功能,并确定加密还是解密
3)进行明文的填充和解填充,调用CBC函数进行加解密

3.2.2 其他重要函数说明

1)bitset<32> part_F(bitset<32> R, bitset<48> K)
作用:轮函数F
参数:需要进行运算的字符串R,密钥K
返回:f函数运算后的结果

2)void CShift(int i, bitset<28> &C, bitset<28> &D)
作用:28位密钥循环左移
参数:需要循环左移的左右字符串C, D的引用
返回:无返回值

3)void generateKeys(bitset<64>Key)
作用:生成子密钥
参数:64位的主密钥
返回:无返回值,子密钥存储在全局变量中

4)bitset<64> encryptDES(bitset<64> message, bitset<64>key)
作用:DES加密过程
参数:64位明文和64位的加密密钥
返回:加密后得到的密文

5)bitset<64> decryptDES(bitset<64> cipher, bitset<64>key)
作用:DES解密过程
参数:64位的密文和64位的解密密钥
返回:解密后得到的明文

6)bitset <64> encrypt3DES(bitset <64> M, bitset <64> k1, bitset <64> k2, bitset <64> k3)
作用:3DES加密过程
参数:64位的明文以及加密所需要的密钥K1, K2, K3
返回:加密后得到的密文

7)bitset <64> decrypt3DES(bitset <64> C, bitset <64> k1, bitset <64> k2, bitset <64> k3)
作用:3DES解密过程
参数:64位的密文以及解密所需要的密钥K1, K2, K3

8)string stobit(string str)
作用:将一个字符转化为8位的01字符形式
参数:需要转换的字符数据str
返回:转换后的01字符数据

9)bitset <64> ctoi(string s)
作用:将01字符串存储在在bitset中
参数:需要转换的字符数据s
返回:转换后的bitset数组

10)bitset <64> CBC(bitset <64> data, bitset <64> initV, int model)
作用:CBC模式的加解密过程
参数:需要加解密的数据,初始化向量initV以及判断加密还是解密的标志model
返回:加解密后的结果

3.3 流程图

3.3.1 主函数流程图

    

des cbc在线加解密 des加解密过程_信息安全_06

3.3.2 其他重要函数流程图

3.3.2.1 part_F函数流程图

   

des cbc在线加解密 des加解密过程_信息安全_07

3.3.2.2 generateKeys函数流程图

des cbc在线加解密 des加解密过程_CBC模式_08

3.3.2.3 encryptDES函数流程图

  

des cbc在线加解密 des加解密过程_des cbc在线加解密_09

四、实验结果与截图

  已根据中间数据验证了轮函数、生成子密钥函数等关键函数的正确性,并且能够正确通过老师给出的测试样例(如图10所示),并且可以实现任意文件的加解密。

des cbc在线加解密 des加解密过程_3DES加解密_10


des cbc在线加解密 des加解密过程_CBC模式_11


des cbc在线加解密 des加解密过程_信息安全_12

五、实验总结

  这次实验使我受益匪浅。虽然CBC模式下3DES的流程看似十分复杂,但是通过画流程图的梳理,弄清每一步的细节之后,再动手编码就不再感觉那么困难了。在以后的编程中,也应该运用这种方法,将复杂的问题抽象简单化,理清思路再开始编程,不然编码时代码大量冗余并且逻辑不清,容易出现bug并且难以调试。此次实验使我对3DES的加解密流程有了更深入的理解,也增加了我对密码学有了更浓厚的兴趣,在往后的学习中,我会进一步的探索其中的奥秘,十分感谢老师认真的教学和指导。

六、代码

  DES的加解密以及轮函数和子密钥生成等关键步骤已经根据中间数据测试过,编写正确,3DES以及CBC模式还未验证正确性,请合理食用

#include <iostream>
#include <bitset>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <string.h>
using namespace std;

//初始置换IP
int initPer[] =
{
    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
};

//逆初始置换IP-1
int reverseInitPer[] =
{
    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
};

//置换选择1 64->56
int permuChoice_1[] =
{
    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
};

//置换选择2 56->48
int permuChoice_2[] =
{
    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
};

//左循环移位,除了1,2,9,16,其余都是2
int leftShiftbit[16] =
{
   1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};

//扩展置换E 32->48
int extendPer[] =
{
    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 sBox[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 pBox[] =
{
    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
};

// 轮函数F的一部分 接受32位数据和48位子密钥,返回32位数据(已验证,正确)
bitset<32> part_F(bitset<32> R, bitset<48> K)
{
    bitset<48> expandR;

    //扩展置换 32 -> 48 (已验证,正确)
    //expandR的第i位为R的第extendPer[i] - 1位
    for(int i = 0; i < 48; i ++)
    {
        expandR[i] = R[extendPer[i] - 1];
//        if (i % 6 == 0) cout << " ";
//        cout << expandR[i];
    }
//    cout << endl;

    //与k异或 48位 (已验证,正确)
    expandR = expandR ^ K;

    //S盒替换 48->32位
    bitset<32> res;
    int cnt = 0;
    for(int i = 0; i < 48; i += 6)
    {
        //行号为最高位最低位表示的十进制
        int row = expandR[i] * 2 + expandR[i + 5];
        //列号为中间四位表示的十进制
        int col = expandR[i + 1] * 8 + expandR[i + 2] * 4 + expandR[i + 3] * 2 + expandR[i + 4];
        //选取第i/6个s盒
        int num = sBox[i / 6][row][col];

        //拼接
        bitset<4> tmp = num;
        for (int j = 3; j >= 0; j --) res[cnt ++] = tmp[j];
    }
//    for (int i = 0; i < 32; i ++)
//    {
//        if (i % 4 == 0) cout << " ";
//        cout << res[i];
//    }
//    cout << endl;

    //P置换 32->32
    bitset<32>tmp = res;
    for(int i = 0; i < 32; i ++)
    {
        res[i] = tmp[pBox[i] - 1];
//        if (i % 4 == 0) cout << " ";
//        cout << res[i];
    }
//    cout << endl;

    return res;
}

//28位密钥左移(已验证,正确)
void CShift(int i, bitset<28> &C, bitset<28> &D)
{
    //循环左移一位
    if (leftShiftbit[i] == 1)
    {
        //C0循环左移一位
        int t = C[0];
        for (int j = 0; j < 27; j ++)
        {
            C[j] = C[j + 1];
        }
        C[27] = t;

        //D0循环左移一位
        t = D[0];
        for (int j = 0; j < 27; j ++)
        {
            D[j] = D[j + 1];
        }
        D[27] = t;
    }
    else //循环左移两位
    {
        //C0循环左移两位
        int t1 = C[0], t2 = C[1];
        for (int j = 0; j < 26; j += 2)
        {
            C[j] = C[j + 2];
            C[j + 1] = C[j + 3];

        }
        C[26] = t1;
        C[27] = t2;

        //D0循环左移两位
        t1 = D[0];
        t2 = D[1];
        for (int j = 0; j < 26; j += 2)
        {
            D[j] = D[j + 2];
            D[j + 1] = D[j + 3];

        }
        D[26] = t1;
        D[27] = t2;
    }
}

//生成子密钥(已验证,正确)
bitset<48> keys[16]; //生成的子密钥
void generateKeys(bitset<64>Key)
{
    //置换选择1 64 -> 56 (已验证,正确)
    bitset<56> nkey;
    for (int i = 0; i < 56; i ++)
    {
        nkey[i] = Key[permuChoice_1[i] - 1];
//        if (i % 7 == 0) cout << " ";
//        cout << nkey[i];
    }
//    cout << endl;

    //16轮密钥扩展(已验证,正确)
    for (int i = 0; i < 16; i ++)
    {
        bitset<28> C, D;
        //将nkey分为左右两部分
        for (int j = 0; j < 56; j ++)
        {
            if (j < 28) C[j] = nkey[j];
            else D[j - 28] = nkey[j];
        }

//        cout << "C" << i << ":";
//        for (int j = 0; j < 28; j ++)
//        {
//            cout << C[j];
//        }
//        cout << endl;
//        cout << "D" << i << ":";
//        for (int j = 0; j < 28; j ++)
//        {
//            cout << D[j];
//        }
//        cout << endl;
        CShift(i, C, D);

        //拼接成新key
        for (int j = 0; j < 56; j ++)
        {
            if (j < 28) nkey[j] = C[j];
            else nkey[j] = D[j - 28];
        }

        //置换选择2 56 -> 48 (已验证,正确)
//        cout << "k" << i << ':';
        bitset<48>K;
        for (int j = 0; j < 48; j ++)
        {
            K[j] = nkey[permuChoice_2[j] - 1];
//            if (j % 6 == 0) cout << " ";
//            cout << K[j];
        }
//        cout << endl;

        keys[i] = K;
    }
}

//DES加密过程 (已验证,正确)
bitset<64> encryptDES(bitset<64> message, bitset<64>key)
{
    bitset<64> cipher;
    bitset<32> L, R;

    //密钥拓展,64位密钥拓展为16 * 48位 (已验证,正确)
    generateKeys(key);

    //初始置换IP 64-> 64 (已验证,正确)
    bitset<64> tmp = message;
    for (int i = 0; i < 64; i ++)
    {
        message[i] = tmp[initPer[i] - 1];
//        if (i % 4 == 0) cout << " ";
//        cout << message[i];
    }
//    cout << endl;

    //十六轮加密:L0 = R0, R0 = L0 ^ F(R0, k1)
    for (int i = 0; i < 16; i ++)
    {
        //先将message分为LR两段
        for (int j = 0; j < 64; j ++)
        {
            if (j < 32) L[j] = message[j];
            else R[j - 32] = message[j];
        }

        bitset <32> t = R;
        R = L ^ part_F(R, keys[i]);
        L = t;

        for (int j = 0; j < 64; j ++)
        {
            if (i == 15)
            {
                if (j < 32) message[j] = R[j];
                else message[j] = L[j - 32];
            }
            else
            {
                if (j < 32) message[j] = L[j];
                else message[j] = R[j - 32];
            }
//            if (j % 8 == 0) cout << ' ';
//            cout << message[j];
        }
//        cout << endl;
    }

    //初始逆置换 (已验证,正确)
    for (int i = 0; i < 64; i ++)
    {
        cipher[i] = message[reverseInitPer[i] - 1];
//        if (i % 8 == 0) cout << " ";
//        cout << cipher[i];
    }
//    cout << endl;

    return cipher;
}

//DES解密 (已验证,正确)
bitset<64> decryptDES(bitset<64> cipher, bitset<64>key)
{
    bitset<64> message;
    bitset<32> L, R;

    //密钥拓展,64位密钥拓展为16 * 48位
    generateKeys(key);

    //初始逆置换
    bitset<64> tmp = cipher;
    for (int i = 0; i < 64; i ++) cipher[reverseInitPer[i] - 1] = tmp[i];

    //十六轮加密:L0 = R1 ^ F(L1, k1), R0 = L1
    for (int i = 15; i >= 0; i --)
    {
        //先将cipher分为LR两段
        for (int j = 0; j < 64; j ++)
        {
            if (i == 15)
            {
                if (j < 32) R[j] = cipher[j];
                else L[j - 32] = cipher[j];
            }
            else
            {
                if (j < 32) L[j] = cipher[j];
                else R[j - 32] = cipher[j];
            }
        }

        bitset <32> t = L;
        L = R ^ part_F(L, keys[i]);
        R = t;

        for (int j = 0; j < 64; j ++)
        {
            if (j < 32) cipher[j] = L[j];
            else cipher[j] = R[j - 32];
        }
    }

    //初始置换
    for (int i = 0; i < 64; i ++) message[initPer[i] - 1] = cipher[i];

    return message;
}

//3DES加密
bitset <64> encrypt3DES(bitset <64> M, bitset <64> k1, bitset <64> k2, bitset <64> k3)
{
    bitset <64> C;
    C = encryptDES(M, k1);
    C = decryptDES(C, k2);
    C = encryptDES(C, k3);
    return C;
}

//3DES解密
bitset <64> decrypt3DES(bitset <64> C, bitset <64> k1, bitset <64> k2, bitset <64> k3)
{
    bitset <64> M;
    M = decryptDES(C, k1);
    M = encryptDES(M, k2);
    M = decryptDES(M, k3);
    return M;
}

string stobit(string str) //将每一个字符转换为8为二进制
{
    unsigned char k = 0x80;
    int length = str.size();
    string bit = "";
    for (int i = 0; i < length; i++)
    {
        for (int j = 0; j < 8; j++, k >>= 1)
        {
            //网上所查,原理还在探究
            //在函数bai中,先将1左移7位,就可以得到二进制值10000000,
            // 再将此值与一个字符“按位与”,然后判断其值是否为0。不为0就输出1,
            // 否则就输出0。类似的操作做8次,就可以输出一个字节的8位二进制码了。
            if (str[i] & k)
            {
                bit += '1';
            }
            else
            {
                bit += '0';
            }
        }
        k = 0x80;
    }
    return bit;
}

//将64位01字符串转换为01数字
bitset <64> ctoi(string s)
{
    bitset<64> M;
    for (int i = 0; i < 64; i ++)
    {
        if (s[i] == '1') M[i] = 1;
        else M[i] = 0;
    }
    return M;
}

bitset <64> CBC(bitset <64> data, bitset <64> initV, int model)
{
    data ^= initV;
    string key1 = "01519800";
    string key2 = "12345678";
    string key3 = "32897592";
    bitset <64> k1 = ctoi(stobit(key1));
    bitset <64> k2 = ctoi(stobit(key2));
    bitset <64> k3 = ctoi(stobit(key3));

    bitset <64> res;
    if (model == 1) res = encrypt3DES(data, k1, k2, k3);
    else res = decrypt3DES(data, k1, k2, k3);
    return res;
}
int main()
{
    //目前DES加解密已验证正确
    string in_filename = "C:\\Users\\Y\\Desktop\\信息安全\\input.txt";
    string out_filename = "C:\\Users\\Y\\Desktop\\信息安全\\output.txt";

    ifstream in;
    in.open(in_filename);
    ofstream out;
    out.open(out_filename);

    string s;
    in >> s;
    in.close();

    puts("加密请按1,解密请按2");
    int model = 0;

    while (cin >> model)
    {
        if (model != 1 && model != 2)
        {
            puts("输入不正确,请重输");
        }
        else
        {
            if (model == 1)
            {
                cout << "您所要加密的文件是" << in_filename <<endl;
                puts("加密成功!");
            }
            else
            {
                cout << "您所要解密的文件是" << in_filename <<endl;
                puts("解密成功!");
            }
            break;
        }
    }

    //填充
    int fillsize = 8 - (s.size() % 8);
    for (int i = 0; i < fillsize; i ++)
    {
        if (i == fillsize - 1)
        {
            int t = fillsize;
            s += char(t);
        }
        else
        {
            int t = 0;
            s += char(t);
        }
    }

    string bitdata = stobit(s);

    puts("结果如下");
    string initvector = "12358789";
    bitset <64> initV = ctoi(stobit(initvector));
    bitset <64> pretext = initV;
    for (int i = 0; i < bitdata.size(); i += 64)
    {
        string t = bitdata.substr(i, 64);
        bitset <64> tdata = ctoi(t);

        bitset <64> res = CBC(tdata, pretext, model);
        pretext = res;
        cout << res << endl;
        out << res;
    }
    out.close();
    return 0;
}