CBC模式的3DES加解密(课程设计报告)

news2024/11/15 9:29:13

目录

  • 一、实验内容
  • 二、实验原理
    • 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所示。
在这里插入图片描述

图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所示。
在这里插入图片描述
  图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)

在这里插入图片描述
  图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拼接即得到密文

在这里插入图片描述

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

图 5 CBC解密流程图

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 主函数流程图

    在这里插入图片描述

3.3.2 其他重要函数流程图

3.3.2.1 part_F函数流程图

   在这里插入图片描述

3.3.2.2 generateKeys函数流程图

在这里插入图片描述

3.3.2.3 encryptDES函数流程图

  在这里插入图片描述

四、实验结果与截图

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

五、实验总结

  这次实验使我受益匪浅。虽然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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并行训练方法-单机多卡

一、简单方便的 nn.DataParallel DataParallel 可以帮助我们&#xff08;使用单进程控&#xff09;将模型和数据加载到多个 GPU 中&#xff0c;控制数据在 GPU 之间的流动&#xff0c;协同不同 GPU 上的模型进行并行训练&#xff08;细粒度的方法有 scatter&#xff0c;gather …

学习记录670@项目管理之变更管理

变更的分类 按变更性质分为重大变更、重要变更和一般变更&#xff0c;可通过不同审批权限控制。按变更的迫切性分为紧急变更和非紧急变更&#xff0c;可通过不同的变更处理流程进行控制。按变更所发生的领域和阶段&#xff0c;可分为进度变更、成本变更、质量变更、设计变更、…

3小时精通opencv(四) 透视变换与图像拼接

3小时精通opencv(四) 透视变换与图像拼接 参考视频资源:3h精通Opencv-Python 文章目录3小时精通opencv(四) 透视变换与图像拼接透视变换图像拼接全部代码透视变换 透视变换建立两平面场之间的对应关系&#xff0c; 将原始图片投影到一个新的视平面上 # Author : JokerTon…

Elasticsearch7.8.0版本入门——JavaAPI操作(环境准备)

目录一、创建springboot项目二、pom.xml文件引入相关maven依赖三、创建客户端对象一、创建springboot项目 创建springboot项目步骤参考此博文链接&#xff1a;https://wwwxz.blog.csdn.net/article/details/91977374 二、pom.xml文件引入相关maven依赖 引入elasticsearch依赖…

NR PUSCH(七) 相干传输

微信同步更新欢迎关注同名mode协议笔记 这篇就是为记录一个概念在协议中的体现方式。相干传输被定义为一种UE能力。考虑到UE的实现成本&#xff0c;NR不要求所有的UE都能做到所有的天线端口都可以相干传输。NR定义了以下3种UE的相干传输能力。 (1)全相干&#xff08;Fully-coh…

正点原子Linux驱动第三期

目录 第一讲 系统镜像烧写 第二讲 u-boot编译 第三讲 uboot命令使用 第四讲 Uboot源码分析 第五讲 uboot顶层 Makefile分析 第六讲 Uboot启动流程 第七讲 uboot移植 第八讲 UBoot图形化界面配置 第九讲 Linux 内核移植 第十讲 Linux内核源码目录分析 第十一讲 Linux…

六、python操作mysql篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频&#xff1a;https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 1. 下载pymysql 2. 新建数据库 3. mysql服务器版本查询 4. 执行非查询性质的SQL 5. 执行查询性质的sql 6. 执行新增sql 1. 下载pymysql 右下角点击版本 选择解释器设置 点击加号 搜…

Python(for和while)循环嵌套及用法

Python 不仅支持 if 语句相互嵌套&#xff0c;while 和 for 循环结构也支持嵌套。所谓嵌套&#xff08;Nest&#xff09;&#xff0c;就是一条语句里面还有另一条语句&#xff0c;例如 for 里面还有 for&#xff0c;while 里面还有 while&#xff0c;甚至 while 中有 for 或者 …

无需下载就能使用的图像编辑器?能代替 Photoshop 吗?#Photopea

一款在线就能使用的图像编辑器&#xff0c;好用&#xff1f;还免费&#xff1f;Photopea 图源: Photopea 官网Photopea 可以处理光栅图形和矢量图形。您可以将它用于简单的任务&#xff0c;例如调整图像大小&#xff0c;也可以用于复杂的任务&#xff0c;例如设计网页、创建插图…

Day05 - 内置函数和参数

0. 列表 推导式 格式: 列表变量 [表达式 for 变量 in range(10)] 表达式中需要使用后面的变量 列表推导 式 创建一个具有一百个数字的列表 # c_l [] # for i in range(100): # c_l.append(i)# 使用列表推导式来完成列表的创建 c_l [i for i in range(100)] # c_l [x f…

macOS Ventura 13.2 (22D49) 正式版发布,ISO、IPSW、PKG 下载

macOS Ventura 13 正式版现已发布&#xff01; 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Ventura/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 2023 年 1 月 23 日&#xff08;北京时间 24 日凌晨&#xff09;&#xff0c;macOS Ventura 1…

新年快乐—数睿通2.0数据中台全新功能模块发布

文章目录引言数据集成数据库管理文件管理数据接入贴源数据数据开发数据生产sql 作业开发FlinkSql 作业开发调度中心运维中心资源中心配置中心其他模块结语引言 离上次发文已经有接近三个月了&#xff0c;这三个月主要在开发数睿通的数据生产模块&#xff0c;同时优化了一下数据…

网络实验之EIGRP协议

一、EIGRP协议简介 EIGRP:Enhanced Interior Gateway Routing Protocol 即 增强内部网关路由协议。也翻译为 加强型内部网关路由协议。 EIGRP是Cisco公司的私有协议&#xff08;2013年已经公有化&#xff09;。 EIGRP结合了链路状态和距离矢量型路由选择协议的Cisco专用协议&am…

【LeetCode】二叉树的序列化与反序列化 [H](二叉树)

297. 二叉树的序列化与反序列化 - 力扣&#xff08;LeetCode&#xff09; 一、题目 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采…

【SpringCloud】Nacos注册中心、配置中心用法与原理(下)

【SpringCloud】Nacos注册中心、配置中心用法与原理&#xff08;下&#xff09; 上集回顾 二、Nacos 配置中心 1. 统一配置管理 &#xff08;1&#xff09;在 Nacos 中添加配置文件 &#xff08;2&#xff09;从微服务拉取配置 2. 配置热更新 方式一&#xff1a;使用 Re…

华为鸿蒙2.0如何安装谷歌服务框架

第一步: 如果安装过Google套件需要先卸载原有Google套件,设置-应用和服务-应用管理,搜索Google、谷歌等,原有谷歌套件全部卸载掉。(注意,要点击右上角打开“显示系统应用”,未安装过可以直接略过) 第二步:下载鸿蒙文件包(自行找资源下载) 1.打开(1.准备)把里面的…

项目2:使用Yolov5和deepsort实现车辆和行人目标跟踪,实时计算目标运动速度和加速度(有检测超速功能)

项目演示视频 项目演示视频可以跳转到哔哩哔哩观看&#xff1a;https://www.bilibili.com/video/BV1RT411Z7kD/?vd_source805c57038e291405fe38f3adefa0f2d2 项目简介 本项目使用Yolov5DeepSort实现车辆、行人跟踪&#xff0c;并实时统计各类别目标数量&#xff0c;以及测量…

人大金仓数据库的库、模式、表空间之间的关系

库、模式、表空间之间的关系 KES数据库结构图 列出集簇现有的数据库清单 \l 默认数据库的作用介绍 数据库定义以及相关操作 创建数据库并设置参数 设置属主、编码、参照模板template0、连接数为0 create database 数据库名 owner 属主用户名 template template0 encoding …

【二叉搜索树】BST相关题目

BST相关题目二叉搜索树中的众树二叉搜索树节点最小距离两数之和 IV - 输入二叉搜索树总结二叉搜索树中的众树 501.二叉搜索树中的众树 解题思路&#xff1a;中序遍历二叉搜索树&#xff0c;使得结果集是有序的&#xff0c;过程中将众数个数保存下来。利用两个变量&#xff0c…

Java---微服务---SpringCloud(2)

SpringCloud021.Nacos配置管理1.1.统一配置管理1.1.1.在nacos中添加配置文件1.1.2.从微服务拉取配置1.2.配置热更新1.2.1.方式一1.2.2.方式二1.3.配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&#xff09;运行两个UserApplication&am…