Loading... <div class="tip share">请注意,本文编写于 1819 天前,最后修改于 1756 天前,其中某些信息可能已经过时。</div> 这学期选了一门课: 计算机安全导论。据说主讲密码学,所以就先学习一下相关的知识啦~ 这几天学的是 DES算法。 > 初学只写了 ECB 模式下的 DES。其他的等以后再说吧 XD ## 1. 算法原理 ### 1.1 什么是 DES ? 数据加密标准(英语:Data Encryption Standard,缩写为 DES)是一种对称密钥加密块密码算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。它基于使用56位密钥的对称算法。这个算法因为包含一些机密设计元素,相对短的密钥长度以及怀疑内含美国国家安全局(NSA)的后门而在开始时有争议,DES因此受到了强烈的学院派式的审查,并以此推动了现代的块密码及其密码分析的发展。——Wikipedia ### 1.2 算法流程 DES 是一个分组加密算法,典型的 DES 以64位为分组对数据加密,加密和解密用的是同一个算法。其大致加密流程如下图: ![des.jpg](https://blog.domineto.top/usr/uploads/2019/09/1688837830.jpg) 下面我来慢慢分析这些都是什么意思吧~ ## 2. 算法实现 ### 2.1 IP置换 IP置换目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位。 置换规则如下表所示: ```cpp // 初始置换表 const int IP[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 }; ``` 表中的数字代表新数据中此位置的数据在原数据中的位置,即原数据块的第58位放到新数据的第1位,第50位放到第2位,……依此类推。置换后的数据分为 L0 和 R0 两部分, L0 为新数据的左32位,R0 为新数据的右32位。 !> 要注意一点,位数是从左边开始数的,即最0x0000 0080 0000 0002最左边的位为1,最右边的位为64。 ### 2.2 生成子密钥 从流程图中可以看出,我们在进行下一步加密的时候需要一个密钥 K,且之后的每一轮都需要密钥。我们知道 DES 需要输入一个64位的密钥,那剩余的密钥就肯定是从这个密钥变化来的。下面就讨论一下这些子密钥是怎么来的。 #### 2.2.1 去除奇偶校验位 不考虑每个字节的第8位,将 DES 的密钥由64位减至56位,每个字节的第8位作为奇偶校验位。产生的56位密钥由下表生成(注意表中没有8,16,24,32,40,48,56和64这8位): ```cpp // 密钥置换表,将64位密钥变成56位 const int PC_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 }; ``` #### 2.2.2 循环左移 在DES的每一轮中,从56位密钥产生出不同的48位子密钥,确定这些子密钥的方式如下: 1. 将56位的密钥分成两部分,每部分28位。 2. 根据轮数,这两部分分别循环左移1位或2位。每轮移动的位数如下表: ```cpp // 每轮左移的位数 const int shiftBits[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; ``` #### 2.2.3 压缩置换 移动后,从56位中选出48位。这个过程中,既置换了每位的顺序,又选择了子密钥,因此称为压缩置换。压缩置换规则如下表(注意表中没有9,18,22,25,35,38,43和54这8位): ```cpp // 压缩置换,将56位密钥压缩成48位子密钥 const int PC_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 }; ``` 至此,新的密钥就已经计算出来了。按照这个方法进行16轮,就可以得到16个子密钥。 ### 2.3 加密处理-迭代 这个过程中数据和子密钥将通过 f 函数结合在一起。下面来分析一下这个 f 函数。 #### 2.3.1 E扩展置换 我们上面生成的子密钥都是48位的,而输入的数据则是32位的,所以我们需要将数据扩展成48位再进行运算。 扩展置换目的有两个: - 生成与密钥相同长度的数据以进行异或运算; - 提供更长的结果,在后续的替代运算中可以进行压缩。 扩展的原理如下表: ```cpp // 扩展置换表,将 32位 扩展至 48位 const int E[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 }; ``` #### 2.3.2 S盒替换 压缩后的密钥与扩展分组异或以后得到48位的数据,将这个数据送人S盒,进行替代运算。替代由8个不同的S盒完成,每个S盒有6位输入4位输出。48位输入分为8个6位的分组,一个分组对应一个S盒,对应的S盒对各组进行代替操作。 > 一个S盒就是一个4行16列的表,盒中的每一项都是一个4位的数。S盒的6个输入确定了其对应的输出在哪一行哪一列,**输入的高低两位做为行数H,中间四位做为列数L,在S-BOX中查找第H行L列对应的数据(<32)**。 ```cpp // S盒,每个S盒是4x16的置换表,6位 -> 4位 const int S_BOX[8][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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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} } }; ``` 代替过程产生8个4位的分组,组合在一起形成32位数据。 > S盒代替是 DES 算法的关键步骤,所有的其他的运算都是线性的,易于分析,而S盒是非线性的,相比于其他步骤,提供了更好安全性。 #### 2.3.3 P盒置换 S盒代替运算的32位输出按照P盒进行置换。该置换把输入的每位映射到输出位,任何一位不能被映射两次,也不能被略去,映射规则如下表: ```cpp // P置换,32位 -> 32位 const int P[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 }; ``` 最后,P盒置换的结果与最初的64位分组左半部分L0异或,然后左、右半部分交换。 至此,f 函数就执行了一遍。按照上述方法执行16轮即可。 ### 2.4 IP-1末置换 末置换是初始置换的逆过程,DES最后一轮后,左、右两半部分并未进行交换,而是两部分合并形成一个分组做为末置换的输入。末置换规则如下表: ```cpp // 结尾置换表 const int IP_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 }; ``` !> 注意合并的时候顺序不能反,R16 在前,L16 在后。 经过以上步骤即可得到密文。 ## 3. C++ 代码实现 网上找的代码好多都是有问题的,搞得我对着python的代码 debug 了几天才修完。这里放上我写的 DES 加密代码吧。有问题的话希望大佬指正。(不要问为什么用C++不用python,问就是快 (ಡωಡ) ) > DES.h ```cpp #pragma once #include <iostream> #include <bitset> #include <string> #include <vector> #define ECB 0 // 电子密码本模式(Electronic Codebook Book) #define CBC 1 // 密码分组链接模式(Cipher Block Chaining) #define CFB 2 // 密码反馈模式(Cipher FeedBack) #define OFB 3 // 输出反馈模式(Output FeedBack) #define CTR 4 // 计算器模式(Counter) using std::bitset; using std::string; using std::vector; using std::cout; using std::endl; const int BLOCK_SIZE = 8; class DES { private: // 输入的64位密钥和子密钥 bitset<64> key; bitset<48> subKey[16]; string OFB_stream; // OFB 加密模式用的流 vector<string> CTR_stream; // CTR 加密模式用的流 // 初始置换表 const int IP[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 }; // 结尾置换表 const int IP_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 }; /*------------------下面是生成密钥所用表-----------------*/ // 密钥置换表,将64位密钥变成56位 const int PC_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 }; // 压缩置换,将56位密钥压缩成48位子密钥 const int PC_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 }; // 每轮左移的位数 const int shiftBits[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; /*------------------下面是密码函数 f 所用表-----------------*/ // 扩展置换表,将 32位 扩展至 48位 const int E[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盒,每个S盒是4x16的置换表,6位 -> 4位 const int S_BOX[8][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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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} }, { {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置换,32位 -> 32位 const int P[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 }; /**********************************************************************/ /* */ /* 下面是DES算法实现所需的函数 */ /* */ /**********************************************************************/ protected: bitset<32> f(bitset<32> R, bitset<48> k); // 加密时的 f 函数 static bitset<28> left_shift(bitset<28> k, int shift); // 循环左移 static string padding(string str, const int& unit = 8); string depadding(const string& str); void generate_key(); // 生成子密钥 bitset<64> _encrypt(bitset<64> & plain); // 对 64位 数据进行加密用的函数 bitset<64> _decrypt(bitset<64> & plain); // 对 64位 数据进行解密用的函数 string ECB_encryption(const string& plain); string ECB_decryption(const string& cipher); string CBC_encryption(const string& plain, const string& IV); string CBC_decryption(const string& cipher, const string& IV); string CFB_encryption(const string& plain, const string& IV, const int& segment_size = 8); string CFB_decryption(const string& cipher, const string& IV, const int& segment_size = 8); string OFB_encryption(const string& plain, const string& IV); string OFB_decryption(const string& cipher, const string& IV); string CTR_encryption(const string& plain, const string& nonce); string CTR_decryption(const string& cipher, const string& nonce); public: DES(); explicit DES(const string& key); ~DES(); /* 工具函数 */ static bitset<64> char_to_bitset(const char s[8]); static string bitset64_to_string(bitset<64> bit); static string bitset64_to_hex(bitset<64> bit); bool set_key(const string& _key); string encrypt(const string& plain, int mode = ECB, const string& IV = "", const int& segment_size = 8); string decrypt(const string& cipher, int mode = ECB, const string& IV = "", const int& segment_size = 8); }; /** * \brief string 转换为 hex */ inline string string2hex(const string& in) { string res; for (auto i : in) { int tmp = static_cast<int>(i); int high = (tmp & 0xf0) >> 4; int low = (tmp & 0x0f); res += (high < 10 ? '0' + high : 'a' + high - 10); res += (low < 10 ? '0' + low : 'a' + low - 10); } return res; } inline string hex2string(const string& in) { // 长度不是偶数说明有问题 if (in.length() % 2 != 0) throw "Hex length error!"; string res; for (auto i = 0; i < in.length(); i += 2) { int tmp = 0; tmp += (in[i] - '0' > 10) ? (in[i] - 'a' + 10) * 16 : (in[i] - '0') * 16; tmp += (in[i + 1] - '0' > 10) ? (in[i + 1] - 'a' + 10) : (in[i + 1] - '0'); res += static_cast<char>(tmp); } return res; } ``` > DES.cpp ```cpp #include "DES.h" /** * \brief 密码函数 f * \param R 输入的 32 位数据 * \param k 48 位子密钥 * \return 32 位的输出 */ bitset<32> DES::f(bitset<32> R, bitset<48> k) { bitset<48> expandR; //第一步: 扩展置换 R for (auto i = 0; i < 48; i++) expandR[i] = R[E[i] - 1]; //第二步: 异或 expandR ^= k; //第三步: 送入S盒 置换 int pos = 0; //在 output 中应输入的位置 bitset<32> output; for (auto i = 0; i < 48; i += 6) { // 计算 S盒 中的行列 int row = expandR[i] * 2 + expandR[i + 5]; int column = expandR[i + 1] * 8 + expandR[i + 2] * 4 \ + expandR[i + 3] * 2 + expandR[i + 4]; int num = S_BOX[i / 6][row]<div class="flex-column"></div>; bitset<4> tmp(num); for (auto j = 0; j < 4; j++) output[pos + j] = tmp[3 - j]; pos += 4; } // 第四步: P置换 bitset<32> tmp = output; for (auto i = 0; i < 32; i++) output[i] = tmp[P[i] - 1]; return output; } /** * \brief 对密钥的两部分进行左移 * \param k 56位密钥的一部分 * \param shift 左移位数 * \return 位移后的数据 */ bitset<28> DES::left_shift(bitset<28> k, int shift) { bitset<28> tmp = k; for (auto i = 0; i < 28; i++) k[i] = tmp[(i + shift) % 28]; return k; } /** * \brief 生成16个48位子密钥 */ void DES::generate_key() { bitset<56> realKey; bitset<28> left, right; bitset<48> compressKey; // 去除奇偶校验位 for (auto i = 0; i < 56; i++) realKey[i] = key[PC_1[i] - 1]; // 生成子密钥 for (auto round = 0; round < 16; round++) { // 前28位 与 后28位 for (auto i = 28; i < 56; i++) right[i - 28] = realKey[i]; for (auto i = 0; i < 28; i++) left[i] = realKey[i]; // 左移 left = left_shift(left, shiftBits[round]); right = left_shift(right, shiftBits[round]); // 压缩置换 for (auto i = 28; i < 56; i++) realKey[i] = right[i - 28]; for (auto i = 0; i < 28; i++) realKey[i] = left[i]; for (auto i = 0; i < 48; i++) compressKey[i] = realKey[PC_2[i] - 1]; subKey[round] = compressKey; } return; } /** * \brief 对明文进行 padding */ string DES::padding(string str, const int& unit) { if (unit > 16) throw "Unit error!"; const int n = unit - (str.length() % unit); const char pad = static_cast<char>(n); for (auto i = 0; i < n; i++) str += pad; return str; } /** * \brief 去除 padding */ string DES::depadding(const string& str) { string res = str; const auto mark = str[str.length() - 1]; for (auto i = 0; i < mark; i++) res.pop_back(); return res; } /** * \brief DES加密 * \param plain 输入的明文 * \param status 真为加密,假为解密 * \return 加密后的密文 */ bitset<64> DES::_encrypt(bitset<64> & plain) { bitset<64> cipher, currentBits; bitset<32> left, right; // 第一步: 置换IP for (auto i = 0; i < 64; i++) currentBits[i] = plain[IP[i] - 1]; // 第二步: 获取 L0 和 R0 for (auto i = 0; i < 32; i++) left[i] = currentBits[i]; for (auto i = 32; i < 64; i++) right[i - 32] = currentBits[i]; // 第三步: 十六轮迭代 for (const auto k : subKey) { const auto new_left = right; right = left ^ f(right, k); left = new_left; } // 第四步: 合并 R16 和 L16 (顺序不能反) for (auto i = 0; i < 32; i++) cipher[i] = right[i]; for (auto i = 32; i < 64; i++) cipher[i] = left[i - 32]; //第五步: 末置换 IP-1 currentBits = cipher; for (auto i = 0; i < 64; i++) cipher[i] = currentBits[IP_1[i] - 1]; //返回密文 return cipher; } bitset<64> DES::_decrypt(bitset<64> & plain) { bitset<64> cipher, currentBits; bitset<32> left, right; // 第一步: 置换IP for (auto i = 0; i < 64; i++) currentBits[i] = plain[IP[i] - 1]; // 第二步: 获取 L0 和 R0 for (auto i = 0; i < 32; i++) left[i] = currentBits[i]; for (auto i = 32; i < 64; i++) right[i - 32] = currentBits[i]; // 第三步: 十六轮迭代 for (auto i = 15; i >= 0; i--) { const auto new_left = right; right = left ^ f(right, subKey[i]); left = new_left; } // 第四步: 合并 R16 和 L16 (顺序不能反) for (auto i = 0; i < 32; i++) cipher[i] = right[i]; for (auto i = 32; i < 64; i++) cipher[i] = left[i - 32]; //第五步: 末置换 IP-1 currentBits = cipher; for (auto i = 0; i < 64; i++) cipher[i] = currentBits[IP_1[i] - 1]; //返回密文 return cipher; } /** * \brief ECB 模式下的 DES 加密 */ string DES::ECB_encryption(const string& plain) { if (plain.length() % BLOCK_SIZE != 0) throw "Plain text's length must be a multiple of 8"; string res; res.clear(); for (auto i = 0; i < plain.length(); i += 8) { string subText(plain, i, 8); bitset<64> tmp = char_to_bitset(subText.c_str()); tmp = _encrypt(tmp); res += bitset64_to_string(tmp); } return res; } /** * \brief ECB 模式下的 DES 解密 */ string DES::ECB_decryption(const string& cipher) { if (cipher.length() % BLOCK_SIZE != 0) throw "Cipher text's length must be a multiple of 8"; string res; res.clear(); for (auto i = 0; i < cipher.length(); i += 8) { string subText(cipher, i, 8); bitset<64> tmp = char_to_bitset(subText.c_str()); tmp = _decrypt(tmp); res += bitset64_to_string(tmp); } return res; } /** * \brief CBC 模式下的 DES 加密 */ string DES::CBC_encryption(const string& plain, const string& IV) { if (plain.length() % BLOCK_SIZE != 0) throw "Plain text's length must be a multiple of 8"; if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; string res; res.clear(); string last = IV; // 需要被异或的字符串 for (auto i = 0; i < plain.length(); i += BLOCK_SIZE) { string subText(plain, i, BLOCK_SIZE); for (auto i = 0; i < BLOCK_SIZE; i++) subText[i] ^= last[i]; bitset<64> tmp = char_to_bitset(subText.c_str()); tmp = _encrypt(tmp); // 更新需要异或的字符串 last = bitset64_to_string(tmp); res += last; } return res; } /** * \brief CBC 模式下的 DES 解密 */ string DES::CBC_decryption(const string& cipher, const string& IV) { if (cipher.length() % BLOCK_SIZE != 0) throw "Cipher text's length must be a multiple of 8"; if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; string res; res.clear(); string last = IV; // 需要被异或的字符串 for (auto i = 0; i < cipher.length(); i += BLOCK_SIZE) { string subText(cipher, i, BLOCK_SIZE); for (auto i = 0; i < BLOCK_SIZE; i++) subText[i] ^= last[i]; bitset<64> tmp = char_to_bitset(subText.c_str()); tmp = _encrypt(tmp); // 更新需要异或的字符串 last = string(cipher, i, BLOCK_SIZE); res += bitset64_to_string(tmp); } return res; } /** * \brief CFB 模式下的 DES 加密 * \param segment_size 每次加密的长度, 单位是位 */ string DES::CFB_encryption(const string& plain, const string& IV, const int& segment_size) { if (segment_size % 8 != 0) throw "Segment size must be a multiple of 8!"; if (plain.length() % BLOCK_SIZE != 0) throw "Plain text's length must be a multiple of 8"; if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; const int real_size = segment_size / 8; string shift_register = IV; // 模拟移位寄存器 string res; for (auto begin = 0; begin < plain.length(); begin += real_size) { // 对移位器中数据进行加密 bitset<64> subText = char_to_bitset(shift_register.c_str()); subText = _encrypt(subText); // 注意这里不是直接保存在移位寄存器中的 string encrypted_buffer = bitset64_to_string(subText); string tmp; // 从Encrypted Buffer左侧取出Real Size个字节,与长度为Real Size的Plain Block进行异或操作 for (auto i = 0; i < real_size; i++) tmp += plain[begin + i] ^ encrypted_buffer[i]; res += tmp; // 将移位器中的数据左移Real Size个字节 shift_register = string(shift_register, real_size); // 将前面的加密结果从右侧移入寄存器 shift_register += tmp; } return res; } /** * \brief CFB 模式下的 DES 解密 * \param segment_size 每次加密的长度, 单位是位 */ string DES::CFB_decryption(const string& cipher, const string& IV, const int& segment_size) { if (segment_size % 8 != 0) throw "Segment size must be a multiple of 8!"; if (cipher.length() % BLOCK_SIZE != 0) throw "Cipher text's length must be a multiple of 8"; if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; const int real_size = segment_size / 8; string shift_register = IV; // 模拟移位寄存器 string res; for (auto begin = 0; begin < cipher.length(); begin += real_size) { // 对移位器中数据进行加密 bitset<64> subText = char_to_bitset(shift_register.c_str()); subText = _encrypt(subText); // 注意这里不是直接保存在移位寄存器中的 string encrypted_buffer = bitset64_to_string(subText); string tmp; // 从Encrypted Buffer左侧取出Real Size个字节,与长度为Real Size的Plain Block进行异或操作 for (auto i = 0; i < real_size; i++) tmp += cipher[begin + i] ^ encrypted_buffer[i]; res += tmp; // 将移位器中的数据左移Real Size个字节 shift_register = string(shift_register, real_size); // 将密文从右侧移入寄存器 shift_register += string(cipher, begin, real_size); } return res; } /** * \brief OFB 模式下的 DES 加密 */ string DES::OFB_encryption(const string& plain, const string& IV) { if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; string res; // 流为空代表之前没有使用过 OFB 加密模式, 需设置 IV if (OFB_stream.empty()) OFB_stream = ECB_encryption(IV); // 如果明文长度小于等于流的长度则直接加密 if (plain.length() <= OFB_stream.length()) { for (auto i = 0; i < plain.length(); i++) res += plain[i] ^ OFB_stream[i]; return res; } // 如果明文长度大于流的长度则继续生成流 for (auto i = OFB_stream.length(); i < plain.length(); i += BLOCK_SIZE) OFB_stream += ECB_encryption(string(OFB_stream, i - BLOCK_SIZE, BLOCK_SIZE)); for (auto i = 0; i < plain.length(); i++) res += plain[i] ^ OFB_stream[i]; return res; } /** * \brief OFB 模式下的 DES 解密。 * 由于明文和密文只在最终的异或过程中使用, 故加密与解密是对称的 */ string DES::OFB_decryption(const string& cipher, const string& IV) { if (IV.length() != BLOCK_SIZE) throw "Initialization vector's length must be 64 bits!"; string res; // 流为空代表之前没有使用过 OFB 加密模式, 需设置 IV if (OFB_stream.empty()) OFB_stream = ECB_encryption(IV); // 如果密文长度小于等于流的长度则直接解密 if (cipher.length() <= OFB_stream.length()) { for (auto i = 0; i < cipher.length(); i++) res += cipher[i] ^ OFB_stream[i]; return res; } // 如果密文长度大于流的长度则继续生成流 for (auto i = OFB_stream.length() - BLOCK_SIZE; i < cipher.length(); i += BLOCK_SIZE) OFB_stream += ECB_encryption(string(OFB_stream, i, BLOCK_SIZE)); for (auto i = 0; i < cipher.length(); i++) res += cipher[i] ^ OFB_stream[i]; return res; } /** * \brief CTR 模式下的 DES 加密 */ string DES::CTR_encryption(const string& plain, const string& nonce) { if (nonce.length() >= BLOCK_SIZE) throw "Nonce's length must be less than 128 bits!"; string res; // 如果明文长度小于等于流的长度则直接加密 if (plain.length() <= OFB_stream.length()) { for (auto i = 0; i < plain.length() / BLOCK_SIZE; i++) { for (auto j = 0; j < BLOCK_SIZE; j++) res += plain[i * BLOCK_SIZE + j] ^ CTR_stream[i][j]; } return res; } // 如果明文长度大于流的长度则继续生成流 for (auto i = CTR_stream.size(); i < plain.length() / BLOCK_SIZE; i++) { // 计数器的值即为 vector 下标的 hex 值 string counter = string2hex(std::to_string(CTR_stream.size())); int counter_len = BLOCK_SIZE - nonce.length(); string input; // 需被加密的输入 if (counter.length() > counter_len) input = nonce + string(counter, counter.length() - counter_len); else { input = nonce; // 补零 for (auto i = 0; i < counter_len - counter.length(); i++) input += static_cast<char>(0); input += counter; } CTR_stream.push_back(ECB_encryption(input)); } for (auto i = 0; i < plain.length() / BLOCK_SIZE; i++) { for (auto j = 0; j < BLOCK_SIZE; j++) res += plain[i * BLOCK_SIZE + j] ^ CTR_stream[i][j]; } return res; } /** * \brief CTR 模式下的 DES 解密 */ string DES::CTR_decryption(const string& cipher, const string& nonce) { if (nonce.length() >= BLOCK_SIZE) throw "Nonce's length must be less than 128 bits!"; string res; // 如果明文长度小于等于流的长度则直接加密 if (cipher.length() <= OFB_stream.length()) { for (auto i = 0; i < cipher.length() / BLOCK_SIZE; i++) { for (auto j = 0; j < BLOCK_SIZE; j++) res += cipher[i * BLOCK_SIZE + j] ^ CTR_stream[i][j]; } return res; } // 如果明文长度大于流的长度则继续生成流 for (auto i = CTR_stream.size(); i < cipher.length() / BLOCK_SIZE; i++) { // 计数器的值即为 vector 下标的 hex 值 string counter = string2hex(std::to_string(CTR_stream.size())); int counter_len = BLOCK_SIZE - nonce.length(); string input; // 需被加密的输入 if (counter.length() > counter_len) input = nonce + string(counter, counter.length() - counter_len); else { input = nonce; // 补零 for (auto i = 0; i < counter_len - counter.length(); i++) input += static_cast<char>(0); input += counter; } CTR_stream.push_back(ECB_encryption(input)); } for (auto i = 0; i < cipher.length() / BLOCK_SIZE; i++) { for (auto j = 0; j < BLOCK_SIZE; j++) res += cipher[i * BLOCK_SIZE + j] ^ CTR_stream[i][j]; } return res; } /** * \brief 将 8 位字符数据转换为 bitset 类型 */ bitset<64> DES::char_to_bitset(const char s[8]) { bitset<64> res; for (auto i = 0; i < 8; i++) { for (auto j = 0; j < 8; j++) res[8 * i + 7 - j] = (s[i] >> j) & 1; } return res; } /** * \brief 将 bitset<64> 类型的数据转换为 string */ string DES::bitset64_to_string(bitset<64> bit) { string res; for (auto i = 0; i < 64; i+=8) { char tmp = 0; for (auto j = 0; j < 8; j++) { tmp = tmp << 1; tmp += bit[i + j]; } res += tmp; } return res; } /** * \brief 将 bitset<64> 类型的数据转换为 HEX */ string DES::bitset64_to_hex(bitset<64> bit) { string res; for (auto i = 0; i < 64; i+=4) { int tmp = 0; for (auto j = 0; j < 4; j++) { tmp = tmp << 1; tmp += bit[i + j]; } if (tmp < 10) res += '0' + tmp; else res += 'a' + tmp - 10; } return res; } /** * \brief 默认构造函数 */ DES::DES() = default; /** * \brief 构造时设置 key * \param _key 传入的密钥 */ DES::DES(const string& _key) { set_key(_key); } DES::~DES() = default; /** * \brief 修改 key 的值 */ bool DES::set_key(const string& _key) { if (_key.length() != BLOCK_SIZE) throw "Key length must be 128 bits!"; // 密钥更换后需要重置两个流的状态 OFB_stream.clear(); CTR_stream.clear(); generate_key(); return true; } string DES::encrypt(const string& plain, const int mode, const string& IV, const int& segment_size) { switch (mode) { case ECB: { try { return ECB_encryption(plain); } catch (const char* msg) { cout << msg << endl; return string(); } } case CBC: { try { return CBC_encryption(plain, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } case CFB: { try { return CFB_encryption(plain, IV, segment_size); } catch (const char* msg) { cout << msg << endl; return string(); } } case OFB: { try { return OFB_encryption(plain, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } case CTR: { try { return CTR_encryption(plain, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } default: { cout << "Unsupported mode!" << endl; return string(); } } } string DES::decrypt(const string& cipher, const int mode, const string& IV, const int& segment_size) { switch (mode) { case ECB: { try { return ECB_decryption(cipher); } catch (const char* msg) { cout << msg << endl; return string(); } } case CBC: { try { return CBC_decryption(cipher, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } case CFB: { try { return CFB_decryption(cipher, IV, segment_size); } catch (const char* msg) { cout << msg << endl; return string(); } } case OFB: { try { return OFB_decryption(cipher, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } case CTR: { try { return CTR_decryption(cipher, IV); } catch (const char* msg) { cout << msg << endl; return string(); } } default: { cout << "Unsupported mode!" << endl; return string(); } } } ``` > 测试用的 main.cpp ```cpp #include <iostream> #include <fstream> #include <chrono> #include "DES.h" #include "3DES.h" #include "base64.h" using namespace std; DES des; TDES tdes; int mode = -1; string k; string IV; string plain_path; string cipher_path; const string method[2] = { "DES", "3DES" }; const string MODE[5] = { "ECB", "CBC", "CFB", "OFB", "CTR" }; int main() { ifstream file; ofstream out; char cmd; int status = 0; // 是否使用 3DES CHANGE_METHOD: while (status == 0) { system("cls"); cout << "DES or 3DES ?" << endl; cout << "1. DES\n2. 3DES" << endl; cin >> status; if (status != 1 && status != 2) { cout << "Method not allow!" << endl; status = 0; system("pause"); } system("cls"); } CHANGE_MODE: while (mode < 0 || mode >= 5) { cout << "请选择加密模式" << endl; cout << "1. ECB\n2. CBC\n3. CFB\n4. OFB\n5. CTR" << endl; cin >> mode; mode--; if (mode < 0 || mode >= 5) { cout << "不支持的加密模式! 请重试" << endl; system("pause"); } system("cls"); } CHANGE_KEY: while (k.empty()) { if (status == 1) { cout << "请输入你的密钥(8个字符)" << endl; cin >> k; if (k.length() < 8) { cout << "密钥不得小于 8 个字符! " << endl; system("pause"); k.clear(); } des.set_key(k); } else if (status == 2) { cout << "请输入你的密钥(24个字符)" << endl; cin >> k; if (!tdes.set_key(k)) { cout << "密钥必须是 24 个字符! " << endl; system("pause"); k.clear(); } } else { cout << "不要乱按哦~" << endl; system("pause"); k.clear(); } system("cls"); } CHANGE_IV: while (IV.empty()) { if (mode == ECB) break; cout << "请输入初始向量 IV: " << endl; cin >> IV; if (IV.length() != BLOCK_SIZE && mode != CTR) { cout << "Initialization vector must be 128 bits!" << endl; IV.clear(); system("pause"); } if (mode == CTR && IV.length() >= BLOCK_SIZE) { cout << "Nonce must be less than 16 bytes!" << endl; IV.clear(); system("pause"); } system("cls"); } system("cls"); while (true) { cout << "当前加密方法为: " << method[status - 1] << endl; cout << "当前加密模式为: " << MODE[mode] << endl; cout << "你的密钥为: " << k << endl; cout << "当前 IV 为: " << IV << endl; cout << "1. 加密\n2. 解密\n3. 修改密钥\n4. 修改加密方式\n5. 修改加密模式\n6. 修改 IV\n7. 退出" << endl; cin >> cmd; switch (cmd) { case '1': { cout << "输入明文所在文件的绝对路径" << endl; cin.get(); getline(cin, plain_path); file.open(plain_path, ios::binary); if (!file.is_open()) { cout << "打开明文文件时发生错误!" << endl; break; } cout << "输入密文输出文件的绝对路径" << endl; getline(cin, cipher_path); out.open(cipher_path, ios::binary); if (!out.is_open()) { cout << "打开输出文件时发生错误!" << endl; break; } // 一次读入整个文件,之后再分组加密 string plain((istreambuf_iterator<char>(file)), istreambuf_iterator<char>()); auto begin = chrono::system_clock::now(); string cipher; if (status == 1) cipher = des.encrypt(plain, mode, IV); else if (status == 2) cipher = tdes.encrypt(plain, mode, IV); auto end = chrono::system_clock::now(); auto duration = chrono::duration_cast<chrono::microseconds>(end - begin); out.write(cipher.c_str(), cipher.length()); file.clear(); file.close(); out.clear(); out.close(); cout << "加密成功!" << endl; cout << "一共花费了" << double(duration.count()) * \ chrono::microseconds::period::num / chrono::microseconds::period::den \ << "秒" << endl; break; } case '2': { cout << "输入密文所在文件的绝对路径" << endl; cin.get(); getline(cin, cipher_path); file.open(cipher_path, ios::binary); if (!file.is_open()) { cout << "打开明文文件时发生错误!" << endl; break; } cout << "输入明文输出文件的绝对路径" << endl; getline(cin, plain_path); out.open(plain_path, ios::binary); if (!out.is_open()) { cout << "打开输出文件时发生错误!" << endl; break; } string cipher((istreambuf_iterator<char>(file)), istreambuf_iterator<char>()); auto begin = chrono::system_clock::now(); string plain; if (status == 1) cipher = des.decrypt(cipher, mode, IV); else if (status == 2) cipher = tdes.decrypt(cipher, mode, IV); auto end = chrono::system_clock::now(); auto duration = chrono::duration_cast<chrono::microseconds>(end - begin); out.write(plain.c_str(), plain.length()); file.clear(); file.close(); out.clear(); out.close(); cout << "解密成功!" << endl; cout << "一共花费了" << double(duration.count())* \ chrono::microseconds::period::num / chrono::microseconds::period::den \ << "秒" << endl; break; } case '3': system("cls"); k.clear(); goto CHANGE_KEY; case '4': system("cls"); status = 0; goto CHANGE_METHOD; case '5': system("cls"); mode = -1; goto CHANGE_MODE; case '6': system("cls"); IV.clear(); goto CHANGE_IV; case '7': return 0; default: cout << "不要瞎按!" << endl; break; } system("pause"); system("cls"); } return 0; } ``` !> 里面的 base64 是我网上找的 base64 编码,这里就不放出来了吧。 ## Referrer https://www.cnblogs.com/songwenlong/p/5944139.html 最后修改:2019 年 11 月 18 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏