DES加密詳解
阿新 • • 發佈:2021-03-03
[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
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 ```