这是一个高内聚低耦合可复用的DES加密系统的实现。
Github 链接:https://github.com/cyendra/CyDES
要实现加密系统,先考虑数据的基本单位。
在DES加密中,数据是以64位为一组的(unsigned long long),我们称它为Bit。
加密过程中会对Bit做位移操作(左移、右移、循环左移、循环右移),也会做位运算(And、Or、Xor),还会做分割以及合并等操作。
我们设计一个类Bit来封装这些操作,作为数据的基本单位。
1 /* 2 表示64位以下的数据 3 可以做常见的位运算 4 */ 5 class Bit { 6 private: 7 unsigned long long bit;// 表示数据,最多64位 8 int size;// 表示数据的位数,不超过64位 9 public: 10 // 值为0的64位数据 11 Bit(); 12 13 // 值为_bit的64位数据 14 Bit(unsigned long long _bit); 15 16 // 值为_bit的n位数据,当n>64位时截断 17 Bit(unsigned long long _bit, int n); 18 19 Bit(const Bit& b); 20 Bit& operator=(Bit& b); 21 //Bit& operator=(unsigned char num); 22 23 // 由byte构造8位Bit对象 24 void Byte(unsigned char num); 25 26 // 直接左移d位 27 void LeftShift(int d); 28 29 // 循环左移d位 30 void LeftRotate(int d); 31 32 // 直接右移d位 33 void RightShift(int d); 34 35 // 循环右移d位 36 void RightRotate(int d); 37 38 // 将第pos位设置为1 39 // pos从左向右从0开始计数,超过size位时无效 40 void Set(int pos); 41 42 // 将所有位都设置为1 43 void Set(); 44 45 // 将第pos位设置为0 46 // pos从左向右从0开始计数,超过size位时无效 47 void Reset(int pos); 48 49 // 将所有位都设置为0 50 void Reset(); 51 52 // 将第pos位取反 53 // pos从左向右从0开始计数,超过size位时无效 54 void Flip(int pos); 55 56 // 将所有位都取反 57 void Flip(); 58 59 // 当所有位都为0时返回true 60 bool None(); 61 62 // 当任意位为1时返回true 63 bool Any(); 64 65 // 统计所有位中1的个数 66 int Count(); 67 68 // 数据的有效位数 69 int Size(); 70 71 // 当第pos位的值为1时返回true 72 // pos从左向右从0开始计数,超过size位时无效 73 bool operator[](int pos); 74 75 // 将数据以unsigned long long的形式返回 76 unsigned long long ToULL(); 77 78 // 将所有二进制位以二进制串形式返回 79 std::string ToString(); 80 81 // 静态方法,将数据bit从中间分割为两份数据 82 static std::pair<Bit, Bit> Split(Bit bit); 83 84 // 静态方法,把数据bit平分为n份,n为偶数,bit的size能被n整除 85 static std::vector<Bit> Split(Bit bit, int n); 86 87 // 静态方法,将两份数据合并为一份数据 88 static Bit Merge(Bit L, Bit R); 89 90 // 静态方法,将Bit数组中从L到R之间的数据合并为一份数据 91 static Bit Merge(Bit b[], int L, int R); 92 93 // 静态方法,求两份数据的异或值 94 static Bit Xor(Bit x, Bit y); 95 96 // 静态方法,求两份数据的OR 97 static Bit Or(Bit x, Bit y); 98 99 // 静态方法,求两份数据的AND 100 static Bit And(Bit x, Bit y); 101 };
DES加密过程的原理其实就是16轮迭代,而迭代过程中有两个重要的处理:子密钥生成和f函数。
所以先不考虑主加密过程,考虑子密钥和f函数的抽象。
虽然子密钥有16之多,但是我们只要有了主密钥就可以生成全部的16个子密钥。
我们把这个过程抽象出来,即输入主密钥,得到一个装有16个子密钥的动态数组:
1 /* 2 主密钥产生子密钥的接口 3 单例模式 4 */ 5 class IKeyManager :public Uncopyable { 6 public: 7 // 通过主密钥产生子密钥 8 virtual std::vector<Bit> GetKeys(Bit MasterKey) = 0; 9 };
接下来考虑f函数,它接受一个32位的数据和一个48位的子密钥,返回32位的加密数据。
1 /* 2 DES中f函数的接口 3 单例模式 4 其值通过32位的数据与48位的子密钥计算而来 5 */ 6 class IFunction :public Uncopyable { 7 public: 8 // f函数 9 // bit为32位的R_{i-1},Key为48位的K_i 10 virtual Bit ProcessKey(Bit bit, Bit Key) = 0; 11 };
有了这两个类,主加密过程就容易实现了。
考虑到主加密过程一次只能处理64位的数据,而实际应用中要多次调用主加密过程来加密长数据。
因此为了避免重复计算子密钥,要在调用主加密过程之前先设置好主秘钥。
解密操作是加密操作的逆操作,所以放在一起实现。
1 /* 2 Data Encryption Standard 接口 3 继承此接口的派生类可以完成以下操作: 4 1-接受一个主密钥 5 2-对一组数据加密 6 3-对一组数据解密 7 */ 8 class IDes { 9 public: 10 // 将64位的key设置为主密钥 11 virtual void SetMasterKey(Bit key) = 0; 12 13 // 加密明文组dat 14 virtual Bit Encryption(Bit dat) = 0; 15 16 // 解密密文组dat 17 virtual Bit Decryption(Bit dat) = 0; 18 };
这样一来,DES的框架就准备好了,实现的时候有两个较复杂的逻辑:置换表操作和S-Box取值操作。
因此单独把这两个操作抽象出来。设计一个类表示置换表,一个类管理置换表,一个类表示S-Box,一个类管理S-Box。
1 /* 2 置换表接口 3 */ 4 class IPermutationTable { 5 public: 6 // 置换表中元素个数 7 virtual int Size() = 0; 8 9 // 获取表中第pos个元素 10 // pos从0开始计数 11 virtual int Get(int pos) = 0; 12 };
1 /* 2 DES中置换表的管理器接口 3 单例模式 4 它管理所有DES中要用到的置换表 5 */ 6 class IPmtTableManager :public Uncopyable { 7 public: 8 // 获取初始置换表IP 9 virtual IPermutationTable* GetIPTable() = 0; 10 11 // 获取逆初始置换表IP-1 12 virtual IPermutationTable* GetIPRevTable() = 0; 13 14 // 获取扩增排列表E 15 virtual IPermutationTable* GetETable() = 0; 16 17 // 获取缩减排列表P 18 virtual IPermutationTable* GetPTable() = 0; 19 20 // 获取密钥排列PC-1 21 virtual IPermutationTable* GetPC1Table() = 0; 22 23 // 获取密钥排列PC-2 24 virtual IPermutationTable* GetPC2Table() = 0; 25 };
1 // S-Box的接口 2 class ISBox { 3 public: 4 // 通过6位数据pos获取S-Box中的值 5 virtual Bit Get(Bit pos) = 0; 6 7 // 通过指定的坐标(i,j)获取S-Box中的值 8 virtual Bit Get(int i, int j) = 0; 9 };
1 /* 2 S-Box管理器的接口 3 单例模式 4 它能够返回指定编号的S-Box 5 */ 6 class ISBoxManager :public Uncopyable { 7 public: 8 // 返回指向编号为i的S-Box的指针 9 virtual SBox* GetSBox(int i) = 0; 10 };
只有置换表还不够,置换操作也是一个较复杂的操作,应该单独抽象出来处理。
1 /* 2 置换操作的接口 3 单例模式 4 */ 5 class IPermutationManager :public Uncopyable { 6 public: 7 // 用置换表table对数据bit进行置换 8 virtual Bit Permutation(Bit bit, IPermutationTable* table) = 0; 9 // 用置换表table对数据bit进行逆置换 10 virtual Bit InversePermutation(Bit bit, IPermutationTable* table) = 0; 11 };
如此一来,在添加具体实现细节以后,DES主加密过程就能顺利实现了。
为了加密长数据,我们为之前的代码套上一层外壳。
用一个类来与用户进行交互,使它可以以用户指定的分组方式来对数据分组并调用主加密过程。
/* 加密器接口 */ class IEncryptor { public: // 分组操作方案 enum Type { ECB, CBC, CFB, OFB }; // 加密字符串 virtual std::vector<unsigned char> EncryptString(const std::string& string, Bit MasterKey, Type type) = 0; // 解密字符串 virtual std::string DecryptString(const std::vector<unsigned char>& bins, Bit MasterKey, Type type) = 0; // 加密二进制串 virtual std::vector<unsigned char> EncryptBinary(const std::vector<unsigned char>& bins, Bit MasterKey, Type type) = 0; // 解密二进制串 virtual std::vector<unsigned char> DecryptBinary(const std::vector<unsigned char>& bins, Bit MasterKey, Type type) = 0; // 加密文件 virtual std::vector<unsigned char> EncryptFile(char *filename, Bit MasterKey, Type type) = 0; // 解密文件 virtual std::vector<unsigned char> DecryptFile(char *filename, Bit MasterKey, Type type) = 0; // 保存二进制串到文件 virtual void SaveBinaryAsFile(const std::vector<unsigned char>& bins, char *filename) = 0; };
最后将具体的分组过程实现即可。
1 /* 2 区块加密法的操作模式接口 3 */ 4 class IMode :public Uncopyable { 5 public: 6 virtual std::vector<unsigned char> EncryptBinary(IDes* des, const std::vector<unsigned char>& bins, Bit MasterKey) = 0; 7 virtual std::vector<unsigned char> DecryptBinary(IDes* des, const std::vector<unsigned char>& bins, Bit MasterKey) = 0; 8 };