1. 程式人生 > >DES加密詳解

DES加密詳解

[TOC] > ***注: 下述所有加法均為模二加, 即為亦或運算*** ## 1 根據輸入的祕鑰得到16個子祕鑰 ### 1.1 大致流程 ```mermaid graph TB; a[輸入一個64位長度的字串,即為祕鑰K_0] b[根據PC-1表挑出56位作為新祕鑰K_1] c[K_1拆分,迴圈左移得到16個子祕鑰k0...k15] d[使用PC-2表從各子祕鑰56位中挑出48位] a-->b-->c-->d ```
圖1.1.1 演算法流程
![image-20210301204737481](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210301204737481.png)
圖1.1.2 PC-1表及PC-2表
### 1.2 利用PC-1從K_0中挑出K_1 ​ 接下來我們以輸入的祕鑰為`"0x123456789abcdeff"`為例, 及二進位制編碼為: > `"0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111"` > > ![image-20210301210922245](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210301210922245.png) > >
圖1.2.1 初始祕鑰K_0
​ 接下來我們利用`PC-1表`生成`新祕鑰K_1` >![image-20210301211558977](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210301211558977.png) >
>
圖1.2.2 挑出的56位二進位制串
> >`"1111 0000 1100 1100 1010 1010 1111"和"1101 0101 1110 0110 1111 1000 1111"` > >兩邊各是28位, 共56位 ### 1.3 利用PC-2從K_1中挑出16個子祕鑰 ​ 對於從`1.2得來的一串56位二進位制碼`, 我們將其拆分成`兩串28位二進位制碼`, 即為: >左半部分的`"1111 0000 1100 1100 1010 1010 1111"`, 並將其命名為C_0 > >右半部分的`"1101 0101 1110 0110 1111 1000 1111"`, 並將其命名為D_0 ​ 接下來, 我們利用下表, 進行迴圈左移, 規則如下: ![image-20210301230226344](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210301230226344.png)
圖1.3.1 位移規則表
​ 之後我們按照上面的規則, 展示`子祕鑰k_1`是如何生成的: >1. 將C_0和D_0分別迴圈左移1位, 得到`C_1-"1110 0001 1001 1001 0101 0101 1111"`以及`D_1-"1010 1011 1100 1101 1111 0001 1111"`**(PS:迴圈左移1位就是將首位移動到最後末尾)** >2. 將C_1即D_1拼接起來, 得到`"1110 0001 1001 1001 0101 0101 1111 1010 1011 1100 1101 1111 0001 1111"` >3. 利用PC-2表從拼接得到的字串中挑選出`子祕鑰k_1`, 類似於"利用PC-1表的過程" > >![image-20210301233109216](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210301233109216.png) > >
圖1.3.2 從C_0和D_0中生成的子祕鑰k_0
> >4. 所以`子祕鑰k_1為"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"` ​ 按照同樣的邏輯, 在C_0和D_0已被迴圈左移的基礎上, 重複上述步驟, 我們得到如下16個子祕鑰: ![image-20210302004853659](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210302004853659.png)
圖1.3.3 生成的所有子祕鑰
​ **PS: 上述輸出為自己寫的程式碼, 在文末會進行展示.** ​ 得到了這16個子祕鑰後, 我們就可以對明文進行加密了, 我們繼續! ## 2 利用16個子祕鑰對明文進行加密 ### 2.1 大致流程 ```mermaid graph TB; a[對64位的明文進行初始變化,簡稱IP-Initial Permutation] b[將IP後的明文平分成32位的L0-左半邊和R0-右半邊] c[接下來使用如下公式對L_n和R_n進行變換] d["1.L_n=R_n-1
2.R_n=L_n-1 + f(R_n-1, k_n)"] e["f的具體過程如下
1.將R_n-1擴充套件成48位並與k_n-1相加"得到S盒
2.使用8張S表將48位的S盒運算成32位輸出P
3.使用P表對輸出P進行位置變換] f["得到輸出L_16和R_16, 拼接成R_16L_16的64位輸出"] g["對這64位輸出使用IP-1表進行位置變換, 即得到最終輸出"] a-->b-->c-->d-.-e-.->f-->g d--重複16次-->d ```
圖2.1.1 演算法流程
### 2.2 將R_0擴充套件成48位的二進位制串, 並與子祕鑰k模二加 ​ 我們以輸入的明文為`"0x0123456789abcdef"`為例, 即二進位制編碼為: >`"0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111"` > >經過變換後,得到: > >`"1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1110 1111 0000 1010 1010"` > >將其拆分為L_0及R_0, 各32位 > >L_0: `"1100 1100 0000 0000 1100 1100 1111 1111"` > >R_0: `"1111 0000 1010 1110 1111 0000 1010 1010"` > >對R_0使用如下規則擴充套件成48位: > >![image-20210302212306838](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210302212306838.png) > >
圖2.2.1 R_0擴充套件成48位規則
> >**PS: 綠色的標出的位數即為擴展出的12位** > >因此, 得到RExtend(8個6位): `"011110 100001 010101 011101 011110 100001 010101 010101"` > >將其與子祕鑰`"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"` > >亦或得S盒`"0110 0001 0001 0111 1011 0010 1000 0110 0110 1100 0010 0011"` ### 2.3 使用S1-S8表將48位變回32位 ​ 為什麼上面S盒要備註**8個6位**呢? 原因就是要以8個6位為輸入, 分別經過S1-S8盒的運算,得到8個4位輸出, 也就變成了32位. >![image-20210302152840419](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210302152840419.png) > >
圖2.3.1 S1-S8表
> >我們的S盒長這樣`"011000 010001 011110 110010 100001 100110 110000 100011"` > >以第一個6位`"011000"`為輸入, 代入到S1表變換 > >以`"011000"`的首位和末尾拼起來的數字為行號, 中間4位為列號(當然都是二進位制的), 即第0(00b)行, 第15(1100b)列, 查詢S1表, 為S1(0, 12) = 5 = 0101b > >這樣就可以得到第一個4位的輸出, 按照這樣的方式可以得到8個4位的輸出, 合在一起就是一個32位的輸出, 經過運算是`"0101 1100 1000 0001 1011 0101 1010 0001"` ### 2.4 使用P表對32位輸出進行位置變換, 並和L0相加 ​ 我們已經得到經過`"擴位"和"縮位"`的變換, 現在要按照P表進行簡單的位置變換: >![image-20210302154240976](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210302154240976.png) > >
圖2.4.1 P表
> >![image-20210302223935952](https://shmily8421.oss-cn-beijing.aliyuncs.com//image/image-20210302223935952.png) > >
圖2.4.2 32位輸出的每一位情況
> >照著P表生成新的32位輸出`"1010 0001 0000 1000 1010 1101 1001 1011"` >再將輸出與L_0相加, L_0:`"1100 1100 0000 0000 1100 1100 1111 1111"` > >得到結果: `"0110 1101 0000 1000 0110 0001 0110 0100"` ### 2.5 往復這樣的步驟16次, 將得到的R_16和L_16拼接並經IP^-1變換 ​ 如題, 得到最終結果: > `"1110 1100 1010 1001 0011 1101 1001 1100 1110 0100 1101 1001 1001 1001 1100 1110"` ## 3 程式碼實現 ```c++ /* * @Description: * @Autor: Shmily * @Date: 2021-03-01 23:40:00 * @LastEditTime: 2021-03-02 22:35:49 */ #include using namespace std; const int8_t PC_1[56] = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 28, 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 }; const int8_t 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 int8_t left_move[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; const int8_t S8[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} } }; const int8_t PTable[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, 4, 22, 11, 4, 25 }; const int8_t IP[2][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, 16, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 }, { 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 } }; void out_int8_t(int8_t* out, int len) { for (int i = 0; i < len; i++) { printf("%d", out[i]); if ((i + 1) % 4 == 0) cout << " "; } cout << endl; } void fun_left_move(int8_t* C_0, int8_t* D_0, int times_left) { for (int i = 0; i < times_left; i++) { int8_t temp1 = C_0[0]; int8_t temp2 = D_0[0]; for (int j = 1; j < 28; j++) { C_0[j - 1] = C_0[j]; D_0[j - 1] = D_0[j]; } C_0[27] = temp1; D_0[27] = temp2; } } void Extend32To48(int8_t (*R) [32], int8_t (*RExtend) [48], int times) { RExtend[times][0] = R[times][31]; RExtend[times][47] = R[times][0]; for (int i = 1; i < 47; i++) { // 6個位一組, 即為一行 int LineNumber = (int)(i / 6); RExtend[times][i] = R[times][i - 1 - LineNumber*2]; } } void XOR(int8_t (*RExtend) [48], int8_t (*k) [48], int8_t (*S) [48], int times) { for (int i = 0; i < 48; i++) { // 進行模二加運算 S[times][i] = (RExtend[times][i] + k[times][i]) % 2; } } void XOR(int8_t (*L) [32], int8_t (*Pchanged) [32], int8_t (*R) [32], int times) { for (int i = 0; i < 32; i++) { R[times + 1][i] = (L[times][i] + Pchanged[times][i]) % 2; } } void UseS48To32(int8_t (*S) [48], int8_t (*P) [32], int times) { for (int i = 0; i < 8; i++) { int line = 0, row = 0; // 計算出行號及列號 line = line + S[times][i*6]*2 + S[times][5 + i*6]; row = row + S[times][1 + i*6]*8 + S[times][2 + i*6]*4 + S[times][3 + i*6]*2 + S[times][4 + i*6]; // 從S表中挑出答案 int8_t ans = S8[i][line][row]; for (int j = 0; j < 4; j++) { int subtrahend = pow(2, (3 - j)); if (ans - subtrahend >= 0) { P[times][j + i*4] = 1; ans -= subtrahend; } else P[times][j + i*4] = 0; } } } void UsePChangeP(int8_t (*P) [32], int8_t (*PChanged) [32], int times) { for (int i = 0; i < 32; i++) { PChanged[times][i] = P[times][PTable[i] - 1]; } } void UseIPChange(int8_t* in, int8_t* out, int8_t choice) { for (int i = 0; i < 64; i++) { out[i] = in[IP[choice][i] - 1]; } } int main(void) { // 對祕鑰進行處理 // 輸入為64位祕鑰, 輸出位16個32位子祕鑰 int8_t K_0[64], K_1[56], k[16][48]; // 祕鑰為"0x123456789abcdeff" char in[] = "0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111"; int len = strlen(in); // 將輸入的祕鑰儲存到K_0中 for (int i = 0, j = 0; i < len; i++) { if (in[i] == '0') K_0[j++] = 0; else if (in[i] == '1') K_0[j++] = 1; } // 利用PC-0表, 從K_0中取出K_1 for (int i = 0; i < 56; i++) { // 注意-1 K_1[i] = K_0[PC_1[i]-1]; } // 上述完成了第一步利用PC-1表 // 接下來利用PC-2表 int8_t C_0[28], D_0[28]; // 將K_1拆分 for (int i = 0; i < 56; i++) { if (i < 28) C_0[i] = K_1[i]; else D_0[i - 28] = K_1[i]; } // 迴圈16次生成16個子祕鑰 for (int i = 0; i < 16; i++) { int8_t times_left = left_move[i]; fun_left_move(C_0, D_0, times_left); for (int j = 0; j < 48; j++) { int8_t sub = PC_2[j] - 1; if (sub < 28) k[i][j] = C_0[sub]; else k[i][j] = D_0[sub - 28]; } // printf("子祕鑰k[%.2d]: ", i + 1); // out_int8_t(k[i], 48); } // 對明文進行處理 // 明文為"0x0123456789abcdef" char in_text[] = "0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111"; int8_t PlainText[64], IP_PlainText[64], EnchiperTextInitial[64], EnchiperText[64]; int8_t L[17][32], R[17][32], RExtend[16][48], S[16][48], P[16][32], PChanged[16][32]; int len_text = strlen(in_text); // 64位明文儲存到PlainText中 for (int i = 0, j = 0; i < len; i++) { if (in_text[i] == '0') PlainText[j++] = 0; else if (in_text[i] == '1') PlainText[j++] = 1; } // 使用IP對PlainText進行變換 UseIPChange(PlainText, IP_PlainText, 0); // 將PlainText分左右儲存到L_0及R_0中 for (int i = 0; i < 64; i++) { if (i < 32) L[0][i] = IP_PlainText[i]; else R[0][i - 32] = IP_PlainText[i]; } // 迴圈16次 for (int times = 0; times < 16; times++) { // L_n = R_n-1 for (int i = 0; i < 32; i++) { L[times + 1][i] = R[times][i]; } // L_n = L_n-1 + f(R_n-1, k_n) // 對R進行擴充套件 Extend32To48(R, RExtend, times); // 將RExtend與k_n-1模二加 XOR(RExtend, k, S, times); // 使用S表將S盒48位壓縮成32位 UseS48To32(S, P, times); // 使用P表對P進行位置變換 UsePChangeP(P, PChanged, times); // 生成的PChanged就是經過f之後的 // 模二加後放入R_n XOR(L, PChanged, R, times); } for (int i = 0; i < 64; i++) { if (i < 32) EnchiperTextInitial[i] = R[16][i]; else EnchiperTextInitial[i] = L[16][i - 32]; } // 使用IP^-1對EnchiperTextInitial的進行變換得到Enchiper UseIPChange(EnchiperTextInitial, EnchiperText, 1); //最終結果輸出 out_int8_t(EnchiperText, 64); return 0