Reverse常见算法

j1ya Lv5

1 byte->2**n bytes

  • 非对称加密:RSA ECC SM2 SM9
  • 哈希:MD5 SHA系列 SM3
  • base58(整除)

1 byte->n bytes

  • 分组:SM1 SM4 SM7
  • base16,32,64,85

1 byte->1 byte

  • RC4

Base64

核心:三转四

强特征:

  • %3
  • &0x3f
  • 61 or =

源码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

def base64_encode(data):
if isinstance(data, str):
data = data.encode("utf-8")
result = []
for i in range(0, len(data), 3):
block = data[i:i + 3]
length = len(block)

b1 = block[0]
b2 = block[1] if length > 1 else 0
b3 = block[2] if length > 2 else 0
value = (b1 << 16) | (b2 << 8) | b3
index1 = (value >> 18) & 0x3F
index2 = (value >> 12) & 0x3F
index3 = (value >> 6) & 0x3F
index4 = value & 0x3F

result.append(BASE64_TABLE[index1])
result.append(BASE64_TABLE[index2])
if length > 1:
result.append(BASE64_TABLE[index3])
else:
result.append("=")
if length > 2:
result.append(BASE64_TABLE[index4])
else:
result.append("=")
return "".join(result)

def base64_decode(encoded):
decode_table = {
char: index
for index, char in enumerate(BASE64_TABLE)
}
result = bytearray()
for i in range(0, len(encoded), 4):
block = encoded[i:i + 4]
c1, c2, c3, c4 = block

v1 = decode_table[c1]
v2 = decode_table[c2]
v3 = decode_table[c3] if c3 != "=" else 0
v4 = decode_table[c4] if c4 != "=" else 0
value = (v1 << 18) | (v2 << 12) | (v3 << 6) | v4
b1 = (value >> 16) & 0xFF
b2 = (value >> 8) & 0xFF
b3 = value & 0xFF
result.append(b1)

if c3 != "=":
result.append(b2)
if c4 != "=":
result.append(b3)
return bytes(result)

print(base64_decode(base64_encode("hello world")))

变表加解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import base64

string1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "0123456789XYZabcdefghijklABCDEFGHIJKLMNOPQRSTUVWmnopqrstuvwxyz+/"

def b64_table_encode(data):
if isinstance(data, str):
data = data.encode()

std_b64 = base64.b64encode(data).decode()
trans = str.maketrans(string1, string2)
return std_b64.translate(trans)


def b64_table_decode(s):
trans = str.maketrans(string2, string1)
std_b64 = s.translate(trans)

std_b64 += "=" * ((4 - len(std_b64) % 4) % 4)

return base64.b64decode(std_b64)


if __name__ == "__main__":
s = "test123"

enc = b64_table_encode(s)
print("encode:", enc)

dec = b64_table_decode(enc)
print("decode str:", dec.decode())

RC4

1.先初始化状态向量S(用来作为密钥流生成的种子1)

2.初始密钥key(由用户输入),长度任意,如果输入长度小于256个字节,则进行循环,直到填满k,有时候k用t来表示

3.开始对状态向量S进行置换操作(用来打乱初始种子1)

KSA

4.生成密钥

PGRA

rc4也可以&255(0xff)

在rc4中,key通常为初始化函数的中间参数,可以快速找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def rc4(data, key):
S = list(range(256))
j = 0
out = []

for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]

i = j = 0
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(char ^ S[(S[i] + S[j]) % 256])

return bytes(out)


data = bytes([0x25, 0x6F, 0x3D, 0x6C, 0xF9, 0xE0, 0xCF, 0x3F, 0x2E, 0x24, 0xC6, 0x7B, 0x81, 0xBF, 0x55, 0x4F, 0x0D, 0x99, 0x87, 0x47, 0x48, 0xF7, 0xB9, 0x98, 0xFB, 0x1B, 0x22, 0xEC, 0x84, 0x23, 0xFD, 0xB2])
key = bytes([0x43]*18)
decrypted = rc4(data, key)
print(decrypted)

%128的魔改本质上就是8位二进制数组成的密钥变为7位

TEA

一种对称算法,每一轮循环使用232位明文和128位密钥(一般是432位)进行加解密

TEA系列算法中均使用一个DELTA常数,但DELTA的值对算法并无什么影响,只是为了避免不良的取值,推荐DELTA的值十六进制值为0x9e3779B9,用于保证每一轮加密都不相同

加密流程

  1. 将明文按64位分组,将每个分组视为一个64位的二进制数,记为V
  2. 将128位的密钥分成4个32位的子密钥,分别记为K0、K1、K2、K3
  3. 将明文分组V分为两个32位的部分,分别记为V0和V1
  4. 设定一个32位的常数delta
  5. TEA算法的加密过程包括32轮迭代,每一轮的加密过程如下:

a. 首先,累加器sum被初始化为0

b. 接着,将delta值加到sum中。delta是一个常数,其值通常为0x9e3779b9,用于增加加密的随机性
c. 然后,将V1左移4位,加上K0,再异或上(V1+sum)和(V1右移5位+K1)的值。得到的结果作为V0的新值
d. 接下来,将V0左移4位,加上K2,再异或上(V0+sum)和(V0右移5位+K3)的值。得到的结果作为V1的新值
e. 重复上述步骤32次,即完成一轮迭代。在每一轮迭代中,V0和V1的值都会被更新,其中V1的更新依赖V0,而V0的更新依赖于V1

  1. 经过32轮迭代后,得到加密后的密文,记为C0和C1
  2. 将C0和C1按顺序连接起来,得到64位的密文

算法注意点

key一般为加密函数第二个参数,sum-=0x61c88647和sum+=0x9e3779b9是等价的

循环里的l和r一般用v0和v1,这样不容易弄混

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define DELTA 0x9e3779b9

void tea_encrypt(unsigned int* v, unsigned int* key) {
unsigned int l = v[0], r = v[1], sum = 0;
for (size_t i = 0; i < 32; i++) {
l += (((r << 4) ^ (r >> 5)) + r) ^ (sum + key[sum & 3]);
sum += DELTA;
r += (((l << 4) ^ (l >> 5)) + l) ^ (sum + key[(sum >> 11) & 3]);
}
v[0] = l;
v[1] = r;
}

//利用可逆性将加密过程逆转
void tea_decrypt(unsigned int* v, unsigned int* key) {
unsigned int l = v[0], r = v[1], sum = 0;
sum = DELTA * 32; l
for (size_t i = 0; i < 32; i++) {
r -= (((l << 4) ^ (l >> 5)) + l) ^ (sum + key[(sum >> 11) & 3]);
sum -= DELTA;
l -= (((r << 4) ^ (r >> 5)) + r) ^ (sum + key[sum & 3]);
}
v[0] = l;
v[1] = r;
}

有些时候伪代码*4但实际上并不需要,因为这时的key是一个字节

注意v0和v1到底是int还是unsigned int

有多步操作的时候一定要注意写for循环的时候也要逆向,下面每次循环都要进行异或,自然得从后往前逆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdint.h>

void decrypt(uint32_t *v, uint32_t *k)
{
uint32_t v0 = v[0], v1 = v[1], i;
uint32_t delta = 0x9e3779b9;
uint32_t sum = delta * 16;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 16; i++)
{
sum -= delta;
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);

}
v[0] = v0; v[1] = v1;
}

unsigned int enc[10] = {
0xBC2B4DF9, 0x6213DD13, 0x89FFFCC9, 0x0FC94F7D, 0x526D1D63, 0xE341FD50, 0x97287633, 0x6BF93638,
0x83143990, 0x1F2CE22C
};

unsigned int key[4] = {
0x12345678, 0x09101112, 0x13141516, 0x15161718
};

int main() {
for (size_t i = 9; i >= 2; i-=2)
{
enc[i] ^= enc[1];
enc[i - 1] ^= enc[0];
decrypt(enc, key);
}
decrypt(enc, key);

printf("%s", enc);
}
// DASCTF{I4TH0ok_I5S0ooFunny_Isnotit?????}

tea算法会有很多魔改的地方,比如循环次数,算法中多异或几个数,下面在tea的前后还实现了交换

如果算法里面异或i的话里面的循环也要逆着写

解密脚本

由于涉及内存指针等问题,大部分用c写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void decrypt(uint32_t v[2], const uint32_t key[4]) {
uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, delta = 0x9e3779b9;
for (int i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]);
v0 -= ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]);
sum -= delta;
}
v[0] = v0;
v[1] = v1;
}

int main() {
uint32_t key[4] = {0x11121314, 0x22232425, 0x33343536, 0x41424344};
uint32_t v5[8] = {0x38b97e28, 0xb7e510c1, 0xb4b29fae, 0x5593bbd7,0x3c2e9b9e, 0x1671c637, 0x8f3a8cb5, 0x5116e515};

for (int i = 0; i < 8; i += 2) {
decrypt(&v5[i], key);
}

for (int i = 0; i < 8; i++) {
for (int m = 0; m <= 3; m++) {
printf("%c", (v5[i] >> (8 * m)) & 0xff);
}
}

return 0;
}

tea加密的另一种模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void decrypt(uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x61C88747, sum = delta * (-64);
for (i=0; i < 64; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3])^v0;
sum += delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3])^v1;

}
v[0] = v0; v[1] = v1;
}

int main() {
uint32_t const k[4] = {0x5377654E,0x21726174,0x5377654E,0x21726174};
uint32_t enc[] = {0xC19EA29C,0xDC091F87,0x91F6E33B,0xF69A5C7A,0x93529F20,0x8A5B94E1,0xF91D069B,0x23B0E340};
for(int i=0; i<8; i+=2){
decrypt(&enc[i], k);
}
for (int i = 0; i < 8; i++) {
for (int m = 0; m <= 3; m++) {
printf("%c", (v5[i] >> (8 * m)) & 0xff);
}
}
return 0;
}

XTEA从做题角度来说差别不大

XXTEA

相比xtea与tea,xxtea的复杂度更上了一层,subkey的方式变为已进行的轮数的最后两位与delta叠加的最后两位相异或,delta迭代与tea相同,常见是9轮循环,循环里面先q再p,q就是循环的次数,为6+52/(n+1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#define MX (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))
#define DELTA 0x9e3779b9

//XXTEA 加密,在处理数据流中每个数据时利用了相邻数据,使用MX函数计算加密值
static uint32_t * xxtea_uint_encrypt(uint32_t * data, size_t len, uint32_t * key) {
uint32_t n = (uint32_t)len - 1;
uint32_t z = data[n], y, p, q = 6 + 52 / (n + 1), sum = 0, e;

if (n < 1) return data;

while (0 < q--) {
sum += DELTA;
// 根据sum 计算得出0~3中的某一个数值, 用于MX中与p共同作用选择key数组中某个秘钥值
e = sum >> 2 & 3;

//遍历每个待加密的数据
for (p = 0; p < n; p++) {
//z的初值为data[len - 1],即将数据数组当做是环形队列来处理的,首尾相连,当加密data[0]时,需要用到data[len - 1],data[0],data[0 + 1],以及MX计算返回的的一个加密值,加密值与data[0]相加后达到加密的效果
y = data[p + 1];
z = data[p] += MX;
}

//当加密data[len-1]时,需要用到data[len - 2],data[len-1],data[0],以及MX计算返回的的一个加密值,加密值与data[len-1]相加后达到加密的效果
y = data[0];
z = data[n] += MX;
}

return data;
}

static uint32_t * xxtea_uint_decrypt(uint32_t * data, size_t len, uint32_t * key) {
uint32_t n = (uint32_t)len - 1;
uint32_t z, y = data[0], p, q = 6 + 52 / (n + 1), sum = q * DELTA, e;

if (n < 1) return data;

while (sum != 0) {
e = sum >> 2 & 3;

for (p = n; p > 0; p--) {
z = data[p - 1];
y = data[p] -= MX;
}

z = data[n];
y = data[0] -= MX;
sum -= DELTA;
}

return data;
}

有些没6+52/(n+1),但还是有其他特征,而且delta也很明显

下面是运行脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) { //加密函数
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++) {
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
} while (--rounds);
} else if (n < -1) { //解密函数
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) {
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}

int main() {
uint32_t v[9] = {689085350, 626885696, 1894439255, 1204672445, 1869189675, 475967424, 1932042439, 1280104741, 2808893494};
uint32_t const k[4] = {12345678, 12398712, 91283904, 12378192};
int n = 9;
btea(v, -n, k);
for (int i = 0; i < 9; i ++) {
printf("%c", v[i] & 0xff);
printf("%c", v[i] >> 8 & 0xff);
printf("%c", v[i] >> 16 & 0xff);
printf("%c", v[i] >> 24 & 0xff);
}
printf("\n");
return 0;
}
import base64
from ctypes import *

enc="F8URvwNjuAYMN6GVXvgJE1Oe2ULWWow0WThUe0k95xTg60OY"
data=list(base64.b64decode(enc))
enc=[int.from_bytes(data[i:i + 4], "little") for i in range(0,len(data),4)]
key=list(map(ord,"0M21I908n117gC1~"))
key=[int.from_bytes(key[i:i + 4], "little") for i in range(0,len(key),4)]


def MX(z, y, sum1, k, p, e):
return c_uint32(((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[(p&3)^e.value]^z.value)))
def btea(v,k,n,delta):

if n>1:
sum1=c_uint32(0)
z=c_uint32(v[n-1])
rounds=6+52//n
e=c_uint32(0)

while rounds>0:
sum1.value+=delta
e.value=((sum1.value>>2)&3)
for p in range(n-1):
y=c_uint32(v[p+1])
v[p] = c_uint32(v[p] + MX(z,y,sum1,k,p,e).value).value
z.value=v[p]

y=c_uint32(v[0])
v[n-1] = c_uint32(v[n-1] + MX(z,y,sum1,k,n-1,e).value).value
z.value=v[n-1]
rounds-=1
else:
sum1=c_uint32(0)
n=-n
rounds=6+52//(n+1)
sum1.value=rounds*delta
y=c_uint32(v[0])
e=c_uint32(0)

while rounds>0:
e.value=((sum1.value>>2)&3)
for p in range(n-1, 0, -1):
z=c_uint32(v[p-1])
v[p] = c_uint32(v[p] - MX(z,y,sum1,k,p,e).value).value
y.value=v[p]

z=c_uint32(v[n-1])
v[0] = c_uint32(v[0] - MX(z,y,sum1,k,0,e).value).value
y.value=v[0]
sum1.value-=delta
rounds-=1

return v

if __name__=='__main__':
a=enc
k= key
delta=0x9e3779b9
n=len(a)
res=btea(a,k,-n,delta)
flag=''
import libnum
for i in res:
flag+=(libnum.n2s(i)[::-1].decode())
print("ISCC{"+flag.strip()+"}")

AES

参考链接:https://zhuanlan.zhihu.com/p/78913397

AES(Advanced Encryption Standard,高级加密标准)的出现主要是为了替代已经无法满足现代安全需求的DES算法。DES采用56bit有效密钥长度,其密钥空间仅为2的56次方,随着计算机计算能力不断提升,暴力破解攻击逐渐变得可行。虽然后来出现了3DES算法,通过多次DES加密提高安全性,但由于其计算成本较高,并且64 bit分组长度限制了其应用范围,因此无法成为长期发展的加密标准

1997年,美国国家标准与技术研究院(NIST)启动高级加密标准(AES)征集计划,希望寻找一种新的分组密码算法替代DES。经过多轮评估,最终有5个候选算法进入最后阶段,分别是Rijndael、Serpent、Twofish、RC6和MARS。经过安全性分析、性能测试以及软硬件实现评估后,Rijndael算法最终被NIST选定,并于2001年正式成为AES标准

不同密钥长度的AES每次处理的数据块大小都是 16 字节

类型 密钥长度 密钥字节数 加密轮数
AES-128 128 bit 16 字节 10 轮
AES-192 192 bit 24 字节 12 轮
AES-256 256 bit 32 字节 14 轮

下面以AES-128为例

加密流程

AES算法主要有四步操作,分别是轮密钥加/密钥加法层(AddRoundKey)、字节代换(SubBytes)、行位移(ShiftRows)、列混淆(MixColumn)

AES算法在轮处理开始前单独进行一次轮密钥加的处理,在处理的轮数上只有最后一轮操作与前面的轮处理有些不同

密钥生成

KeyExpansion对应生成轮密钥

上面图中的k[0]到k[n]就是由这一步产生

子密钥生成以列为单位进行,一列是32位,四列组成子密钥共128位。生成子密钥的数量比AES算法的轮数多一个,因为第一个密钥加法层进行密钥漂白时也需要子密钥。密钥漂白指在AES的输入和输出中都使用的子密钥XOR加法。子密钥在图中都存储在W[0]、W[1]到W[43]的扩展密钥数组之中

k1-k16表示原始密钥对应的字节,在生成的扩展密钥中W的下标如果是4的倍数时需要对异或的参数进行G函数处理

扩展密钥生成有关公式如下

图中子密钥k0与原始子密钥相同

G函数首先将4个输入字节进行翻转,并执行一个按字节的S盒代换,最后用第一个字节与轮系数Rcon进行异或运算。轮系数是一个有10个元素的一维数组,一个元素1个字节。G()函数存在的目的有两个,一是增加密钥编排中的非线性;二是消除AES中的对称性。这两种属性都是抵抗某些分组密码攻击必要的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 用于密钥扩展 Rcon[0]作为填充,没有实际用途
const unsigned int Rcon[11] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };

/**
* @brief 对扩展密钥矩阵的指定列进行 S 盒替换
*/
int Key_S_Substitution(unsigned char (*ExtendKeyArray)[44], unsigned int nCol) {
int ret = 0;

for (int i = 0; i < 4; i++) {
// 使用 S_Table 进行字节替换,高 4 位为行索引,低 4 位为列索引
ExtendKeyArray[i][nCol] = S_Table[(ExtendKeyArray[i][nCol]) >> 4][(ExtendKeyArray[i][nCol]) & 0x0F];
}

return ret;
}

/**
* @brief G 函数:包含循环左移、S 盒替换和异或轮常数
*/
int G_Function(unsigned char (*ExtendKeyArray)[44], unsigned int nCol) {
int ret = 0;

// 1. 字循环 (RotWord):将 nCol-1 列复制到 nCol 列,并循环左移一个字节
for (int i = 0; i < 4; i++) {
ExtendKeyArray[i][nCol] = ExtendKeyArray[(i + 1) % 4][nCol - 1];
}

// 2. 字节代换 (SubWord):将 nCol 列进行 S 盒替换
Key_S_Substitution(ExtendKeyArray, nCol);

// 3. 轮常数异或 (Rcon):将该列第一行元素与 Rcon 进行异或
ExtendKeyArray[0][nCol] ^= Rcon[nCol / 4];

return ret;
}

/**
* @brief 生成完整的扩展密钥矩阵 (AES-128 对应 44 列)
*/
int CalculateExtendKeyArray(const unsigned char (*PasswordArray)[4], unsigned char (*ExtendKeyArray)[44]) {
int ret = 0;

// 1. 初始化:将原始密钥放入扩展密钥组的前四列 (W[0] - W[3])
for (int i = 0; i < 16; i++) {
ExtendKeyArray[i & 0x03][i >> 2] = PasswordArray[i & 0x03][i >> 2];
}

// 2. 计算后续 40 列密钥 (W[4] - W[43])
for (int i = 1; i < 11; i++) { // 进行 10 轮扩展
// (1) 如果列号是 4 的倍数 (即每组的第一列),执行 G 函数
G_Function(ExtendKeyArray, 4 * i);

// (2) 每一轮中,各列进行异和运算
// 处理每组的第一列:W[4i] = W[4(i-1)] ^ G(W[4i-1])
for (int k = 0; k < 4; k++) {
ExtendKeyArray[k][4 * i] = ExtendKeyArray[k][4 * i] ^ ExtendKeyArray[k][4 * (i - 1)];
}

// 处理该轮的其他三列:W[j] = W[j-1] ^ W[j-4]
for (int j = 1; j < 4; j++) {
for (int k = 0; k < 4; k++) {
ExtendKeyArray[k][4 * i + j] = ExtendKeyArray[k][4 * i + j - 1] ^ ExtendKeyArray[k][4 * (i - 1) + j];
}
}
}

return ret;
}

轮密钥加

在密钥加法层中有两个输入的参数,分别是明文和子密钥k,而且这两个输入都是128位的。在%2的扩展域中加减法与异或运算等价,所以这里只需异或

第一轮AddRoundKey

循环AddRoundKey+最后一轮AddRoundKey

字节代换

S盒是一个拥有256个字节元素的数组,可以将其定义为一维数组,也可以将其定义为16×16的二维数组,如果将其定义为二维数组,读取S盒数据的方法就是要将输入数据的每个字节的高四位作为第一个下标,低四位作为第二个下标,从S盒中取出并赋给字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
unsigned char S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16};
unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D};

动态生成S盒代码

x = 2 * x;

if (x & 0x80) x ^= 0x1B;

inv = gf_inverse(x);

y = affine(inv) ^ 0x63;

行位移

只在4×4矩阵的行间进行操作,每行4字节的数据。在加密时,保持矩阵的第一行不变,第二行向左移一个字节、第三行向左移2个字节、第四行向左移3个字节

这里1 -> 5 -> 9 -> 13指的是下标,对应第二行左移1字节

列混淆

列混淆是AES算法中最为复杂的部分,属于扩散层;列混淆操作是AES算法中主要的扩散元素,它混淆了输入矩阵的每一列,使输入的每个字节都会影响到4个输出字节

在加密的正向列混淆中,要将输入的4×4矩阵左乘一个给定的4×4矩阵。而它们之间的加法、乘法都在扩展域GF(2^8)中进行,所以可以将这一个步骤分成两个部分进行讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};

int MixColum(unsigned char(*PlainArray)[4])
{
int ret = 0;

unsigned char ArrayTemp[4][4];

memcpy(ArrayTemp, PlainArray, 16);

for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] =
MixArray[i][0] * ArrayTemp[0][j] +
MixArray[i][1] * ArrayTemp[1][j] +
MixArray[i][2] * ArrayTemp[2][j] +
MixArray[i][3] * ArrayTemp[3][j];
}
}
return ret;
}

在矩阵乘法中,出现了加法和乘法运算,在扩展域中加法操作等同于异或运算,而乘法操作需要一个特殊的方式进行处理,于是先把代码中的加号换成异或符号,然后将伽罗瓦域的乘法定义成一个有两个参数的函数,并返回最后计算结果。于是列混淆的代码就会变成下面的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
char GaloisMultiplication(unsigned char Num_L, unsigned char Num_R){
//定义变量
unsigned char Result = 0; //伽罗瓦域内乘法计算的结果

while (Num_L)
{
//如果Num_L最低位是1就异或Num_R,相当于加上Num_R * 1
if (Num_L & 0x01)
{
Result ^= Num_R;
}

//Num_L右移一位,相当于除以2
Num_L = Num_L >> 1;

//如果Num_R最高位为1
if (Num_R & 0x80)
{
//左移一位相当于乘二
Num_R = Num_R << 1; //注:这里会丢失最高位,但是不用担心

Num_R ^= 0x1B; //计算伽罗瓦域内除法Num_R = Num_R / (x^8(刚好丢失最高位) + x^4 + x^3 + x^1 + 1)
}
else
{
//左移一位相当于乘二
Num_R = Num_R << 1;
}
}
return Result;
}
const unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};

int MixColum(unsigned char(*PlainArray)[4]){
int ret = 0;
//定义变量
unsigned char ArrayTemp[4][4];

//初始化变量
memcpy(ArrayTemp, PlainArray, 16);

//矩阵乘法 4*4
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] =
GaloisMultiplication(MixArray[i][0], ArrayTemp[0][j]) ^
GaloisMultiplication(MixArray[i][1], ArrayTemp[1][j]) ^
GaloisMultiplication(MixArray[i][2], ArrayTemp[2][j]) ^
GaloisMultiplication(MixArray[i][3], ArrayTemp[3][j]);
}
}
return ret;
}

SIMD 向量化写法实现GF(2^8) 线性变换

下面是我对加密流程的一些思考

1.AES最后一轮为什么没有列混淆?

为了让解密算法能够直接复用加密算法的代码逻辑,只需要更换一下 S 盒和列混淆矩阵,而不需要改变每一轮内部的执行顺序

2.为什么AES算法列混淆部分要在有限域下进行?

封闭性保证数据永远是8位不超出范围;数学保证完全可逆;每一位的影响都被均匀扩散

3AES算法有没有什么攻击方法?

DFA 差分故障分析: 对同一个明文加密多次,其中一次正常加密,其他多次在 AES 中间状态人为制造单字节错误。比较正确密文和错误密文的差异,就可以恢复最后一轮轮密钥,见2023巅峰极客初赛m1_read

填充

下面是几种典型的填充方式:

NoPadding: 不做任何填充,但是要求明文必须是16字节的整数倍

PKCS5Padding(默认): 如果明文块少于16个字节,在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。 比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6 }

PKCS7Padding原理与PKCS5Padding相似,区别是PKCS5Padding的blocksize为8字节,而PKCS7Padding的blocksize可以为1到255字节

ISO10126Padding:如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字节,最后一个字符值等于缺少的字符数,其他字符填充随机数。比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则可能补全为{1,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}

模式

AES的工作模式,体现在把明文块加密成密文块的处理过程中,AES加密算法主要提供了六种不同的工作模式

CTR,CFB,OFB都是最后一步异或才用到明文,所以明密文长度相同

iv长度并不固定,当iv长度为 96 bit 时,算法可直接将其与 32 bit 计数器初值 0x00000001 拼接形成初始计数块 J0

解密脚本

标准aes+base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

key = 0x554B134A029DE539438BD18604BF114

key_bytes = key.to_bytes(16, byteorder='big')
#print(key_bytes)

encrypted_data = "2e8Ugcv8lKVhL3gkv3grJGNE3UqkjlvKqCgJSGRNHHEk98Kd0wv6s60GpAUsU+8Q"
encrypted_bytes = base64.b64decode(encrypted_data)
cipher = AES.new(key_bytes, AES.MODE_ECB)

try:
decrypted_data = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
print("解密结果:", decrypted_data.decode('utf-8'))
except (ValueError, KeyError):
print("解密失败或填充错误!")

这是魔改s盒的代码,密文只能16位一组解密,密文按顺序提取即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
class IAES:
global new_s_box
def __init__(self):
self.Nk = 4
self.Nb = 4
self.Nr = 10

def arrays(self, raws):
Nb = []
for i in range(4):
Nb = Nb + [raws[4 * 0 + i], raws[4 * 1 + i], raws[4 * 2 + i], raws[4 * 3 + i]]
return Nb

def Inv_arrays(self, raws):
Inv_raws = []
for i in range(4):
Inv_raws = Inv_raws + [raws[4 * 0 + i], raws[4 * 1 + i], raws[4 * 2 + i], raws[4 * 3 + i]]
return Inv_raws

def view(self, raws):
raws = self.Inv_arrays(raws)
raws = ''.join([x.to_bytes(1, byteorder='big').hex() for x in raws])
print(raws)

def view2(self, list):
for i in range(len(list)):
print(format(list[i], '2x'), end=' ')
if i & 3 == 3: # i%4 == 3
print('\n', end='')
print('\n', end='')

def AddRoundKey(self, raws, Keys):
AddRoundKey = []
for raw, Key in zip(raws, Keys):
AddRoundKey.append(raw ^ Key)
return AddRoundKey

def SubBytes(self, raws):
S_box=new_s_box
raws_S_box = []
for raw in raws:
raws_S_box.append(S_box[raw])
return raws_S_box

def InvSubBytes(self, raws,inv_s_box):
IS_box = inv_s_box
raws_IS_box = []
for raw in raws:
raws_IS_box.append(IS_box[raw])
return raws_IS_box

def InvShiftRows(self, raws):
s13 = raws.pop(7)
raws.insert(4, s13)
s2223 = raws[10:12]
del raws[10:12]
raws[8:0] = s2223
s313233 = raws[13:16]
del raws[13:16]
raws[12:0] = s313233
return raws

def GMUL(self, a, b): # Russian Peasant Multiplication algorithm
p = 0
while a and b:
if b & 1: # b%2
p = p ^ a
if a & 0x80: # a=a*x^7(a>0),a >= 2**7(128)
a = (a << 1) ^ 0x11b # 0x11b = x^8 + x^4 + x^3 + x + 1 (0b100011011)
else:
a = a << 1
b = b >> 1
return p

def InvMixColumns(self, raws):
for i in range(4):
raws[0 * 4 + i], \
raws[1 * 4 + i], \
raws[2 * 4 + i], \
raws[3 * 4 + i] \
= \
self.GMUL(0x0e, raws[0 * 4 + i]) ^ self.GMUL(0x0b, raws[1 * 4 + i]) ^ self.GMUL(0x0d, raws[
2 * 4 + i]) ^ self.GMUL(0x09, raws[3 * 4 + i]), \
self.GMUL(0x09, raws[0 * 4 + i]) ^ self.GMUL(0x0e, raws[1 * 4 + i]) ^ self.GMUL(0x0b, raws[
2 * 4 + i]) ^ self.GMUL(0x0d, raws[3 * 4 + i]), \
self.GMUL(0x0d, raws[0 * 4 + i]) ^ self.GMUL(0x09, raws[1 * 4 + i]) ^ self.GMUL(0x0e, raws[
2 * 4 + i]) ^ self.GMUL(0x0b, raws[3 * 4 + i]), \
self.GMUL(0x0b, raws[0 * 4 + i]) ^ self.GMUL(0x0d, raws[1 * 4 + i]) ^ self.GMUL(0x09, raws[
2 * 4 + i]) ^ self.GMUL(0x0e, raws[3 * 4 + i])
return raws

def RotWord(self, temp):
b0 = temp.pop(0)
temp.insert(3, b0)
return temp

def SubWord(self, temp):
temp = self.SubBytes(temp)
return temp

def KeyExpansion(self, key):
i = 0
w = [[0]] * (self.Nb * (self.Nr + 1))
Rcon = [[0x01, 0x00, 0x00, 0x00],
[0x02, 0x00, 0x00, 0x00],
[0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00],
[0x10, 0x00, 0x00, 0x00],
[0x20, 0x00, 0x00, 0x00],
[0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00],
[0x1B, 0x00, 0x00, 0x00],
[0x36, 0x00, 0x00, 0x00]
]
while i < self.Nk:
w[i] = ([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]])
i = i + 1

i = self.Nk

while i < self.Nb * (self.Nr + 1):
temp = w[i - 1].copy()
if i % self.Nk == 0:
temp = self.SubWord(self.RotWord(temp))
temp2 = []
for temp1, Rcon1 in zip(temp, Rcon[(i // self.Nk) - 1]):
temp2.append(temp1 ^ Rcon1)
temp = temp2
elif self.Nk > 6 and i % self.Nk == 4:
temp = self.SubWord(temp)
w_temp = []
for w1, temp1 in zip(w[i - self.Nk], temp):
w_temp.append(w1 ^ temp1)
w[i] = w_temp
i = i + 1
return w

def IAES(self, IInput, Cipher_Key,inv_s_box):
IInput = [IInput1 for IInput1 in IInput]
Cipher_Key = [Cipher_Key1 for Cipher_Key1 in Cipher_Key]
KeyExpansion = self.KeyExpansion(Cipher_Key)
keys = []
for Key_index in range(len(KeyExpansion) // 4):
keys_temp = (KeyExpansion[4 * Key_index] + KeyExpansion[4 * Key_index + 1] + KeyExpansion[
4 * Key_index + 2] + KeyExpansion[4 * Key_index + 3])
keys_temp = self.arrays(keys_temp)
keys.append(keys_temp)
IInput = self.arrays(IInput)
self.view(IInput)
self.view(keys[-1])
IInput = self.AddRoundKey(IInput, keys[-1])
self.view(IInput)
for index in range(self.Nr - 1):
IInput = self.InvShiftRows(IInput)
self.view(IInput)
IInput = self.InvSubBytes(IInput,inv_s_box)
self.view(IInput)
self.view(keys[-1 - 1 - index])
IInput = self.AddRoundKey(IInput, keys[-1 - 1 - index])
self.view(IInput)
IInput = self.InvMixColumns(IInput)
self.view(IInput)
IInput = self.InvShiftRows(IInput)
self.view(IInput)
IInput = self.InvSubBytes(IInput,inv_s_box)
self.view(IInput)
self.view(keys[0])
IInput = self.AddRoundKey(IInput, keys[0])
self.view(IInput)
IInput = self.Inv_arrays(IInput)
IInput = bytes(IInput)
return IInput


S_box = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]

new_s_box = [0x31, 0x52, 0x5A, 0xC8, 0x0B, 0xAC, 0xF3, 0x3A, 0x8B, 0x54, 0x27, 0x9B, 0xAB, 0x95, 0xDE, 0x83, 0x60, 0xCB,
0x53, 0x7F, 0xC4, 0xE3, 0x0A, 0x97, 0xE0, 0x29, 0xD5, 0x68, 0xC5, 0xDF, 0xF4, 0x7B, 0xAA, 0xD6, 0x42, 0x78,
0x6C, 0xE9, 0x70, 0x17, 0xD7, 0x37, 0x24, 0x49, 0x75, 0xA9, 0x89, 0x67, 0x03, 0xFA, 0xD9, 0x91, 0xB4, 0x5B,
0xC2, 0x4E, 0x92, 0xFC, 0x46, 0xB1, 0x73, 0x08, 0xC7, 0x74, 0x09, 0xAF, 0xEC, 0xF5, 0x4D, 0x2D, 0xEA, 0xA5,
0xDA, 0xEF, 0xA6, 0x2B, 0x7E, 0x0C, 0x8F, 0xB0, 0x04, 0x06, 0x62, 0x84, 0x15, 0x8E, 0x12, 0x1D, 0x44, 0xC0,
0xE2, 0x38, 0xD4, 0x47, 0x28, 0x45, 0x6E, 0x9D, 0x63, 0xCF, 0xE6, 0x8C, 0x18, 0x82, 0x1B, 0x2C, 0xEE, 0x87,
0x94, 0x10, 0xC1, 0x20, 0x07, 0x4A, 0xA4, 0xEB, 0x77, 0xBC, 0xD3, 0xE1, 0x66, 0x2A, 0x6B, 0xE7, 0x79, 0xCC,
0x86, 0x16, 0xD0, 0xD1, 0x19, 0x55, 0x3C, 0x9F, 0xFB, 0x30, 0x98, 0xBD, 0xB8, 0xF1, 0x9E, 0x61, 0xCD, 0x90,
0xCE, 0x7C, 0x8D, 0x57, 0xAE, 0x6A, 0xB3, 0x3D, 0x76, 0xA7, 0x71, 0x88, 0xA2, 0xBA, 0x4F, 0x3E, 0x40, 0x64,
0x0F, 0x48, 0x21, 0x35, 0x36, 0x2F, 0xE8, 0x14, 0x5D, 0x51, 0xD8, 0xB5, 0xFE, 0xD2, 0x96, 0x93, 0xA1, 0xB6,
0x43, 0x0D, 0x4C, 0x80, 0xC9, 0xFF, 0xA3, 0xDD, 0x72, 0x05, 0x59, 0xBF, 0x0E, 0x26, 0x34, 0x1F, 0x13, 0xE5,
0xDC, 0xF2, 0xC6, 0x50, 0x1E, 0xE4, 0x85, 0xB7, 0x39, 0x8A, 0xCA, 0xED, 0x9C, 0xBB, 0x56, 0x23, 0x1A, 0xF0,
0x32, 0x58, 0xB2, 0x65, 0x33, 0x6F, 0x41, 0xBE, 0x3F, 0x6D, 0x11, 0x00, 0xAD, 0x5F, 0xC3, 0x81, 0x25, 0xA8,
0xA0, 0x9A, 0xF6, 0xF7, 0x5E, 0x99, 0x22, 0x2E, 0x4B, 0xF9, 0x3B, 0x02, 0x7A, 0xB9, 0x5C, 0x69, 0xF8, 0x1C,
0xDB, 0x01, 0x7D, 0xFD]

new_contrary_sbox = [0] * 256

for i in range(256):
line = (new_s_box[i] & 0xf0) >> 4

rol = new_s_box[i] & 0xf

new_contrary_sbox[(line * 16) + rol] = i

print(new_contrary_sbox)
IInput = bytes.fromhex('B0CC93EAE92FEF5699396E023B4F9E42')
print(IInput)
Cipher_Key = bytes(b'user01_nkctf2024')
print(Cipher_Key)
Out = IAES().IAES(IInput, Cipher_Key,new_contrary_sbox)
print(Out)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void AddRoundKey(unsigned char *plaintext, unsigned char * CipherKey)/* 轮密钥加 */
{
for (int j = 0; j < 16; j++) plaintext[j] = plaintext[j] ^ CipherKey[j];
}
void SubBytes(unsigned char *plaintext, unsigned char *plaintextencrypt, int count)/*S 盒置换 */
{
unsigned int row, column;
unsigned char Sbox[16][16] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16

};// 填充 Sbox 矩阵
for (int i = 0; i < count; i++)
{
row = (plaintext[i] & 0xF0) >> 4;
column = plaintext[i] & 0x0F;
plaintextencrypt[i] = Sbox[row][column];
}
}
void SubBytesRe(unsigned char *plaintext, unsigned char *plaintextencrypt, int count)/*S 盒逆置换 */
{
unsigned int row, column;
unsigned char Sbox[16][16] = {
82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125 }; // 填充 Sbox 矩阵
for (int i = 0; i < count; i++)
{
row = (plaintext[i] & 0xF0) >> 4;
column = plaintext[i] & 0x0F;
plaintextencrypt[i] = Sbox[row][column];
}
}
void ShiftRowsRe(unsigned char *plaintextencrypt)/* 行移位的逆 */
{
unsigned char temp = 0;
for (int i = 0; i < 4; i++)// 第 i 行
{
for (int j = 0; j < 4 - i; j++)// 第 j 次左移
{
temp = plaintextencrypt[i];
for (int k = 0; k < 4; k++)
plaintextencrypt[i + 4 * k] = plaintextencrypt[i + 4 * (k + 1)];
plaintextencrypt[i + 12] = temp;
}
}
}
void ShiftRows(unsigned char *plaintextencrypt)/* 行移位 */
{
unsigned char temp = 0;
for (int i = 0; i < 4; i++)// 第 i 行
{
for (int j = 0; j < i; j++)// 第 j 次左移
{
temp = plaintextencrypt[i];
for (int k = 0; k < 4; k++)
plaintextencrypt[i + 4 * k] = plaintextencrypt[i + 4 * (k + 1)];
plaintextencrypt[i + 12] = temp;
}
}
}
unsigned char Mult2(unsigned char num)/* 列混淆 */
{
unsigned char temp = num << 1;
if ((num >> 7) & 0x01)
temp = temp ^ 27;
return temp;
}
unsigned char Mult3(unsigned char num)
{
return Mult2(num) ^ num;
}
void MixColumns(unsigned char *plaintextencrypt, unsigned char *plaintextcrypt)
{
int i;
for (i = 0; i < 4; i++)
plaintextcrypt[4 * i] = Mult2(plaintextencrypt[4 * i]) ^ Mult3(plaintextencrypt[4 * i + 1]) ^ plaintextencrypt[4 * i + 2] ^ plaintextencrypt[4 * i + 3];
for (i = 0; i < 4; i++)
plaintextcrypt[4 * i + 1] = plaintextencrypt[4 * i] ^ Mult2(plaintextencrypt[4 * i + 1]) ^ Mult3(plaintextencrypt[4 * i + 2]) ^ plaintextencrypt[4 * i + 3];
for (i = 0; i < 4; i++)
plaintextcrypt[4 * i + 2] = plaintextencrypt[4 * i] ^ plaintextencrypt[4 * i + 1] ^ Mult2(plaintextencrypt[4 * i + 2]) ^ Mult3(plaintextencrypt[4 * i + 3]);
for (i = 0; i < 4; i++)
plaintextcrypt[4 * i + 3] = Mult3(plaintextencrypt[4 * i]) ^ plaintextencrypt[4 * i + 1] ^ plaintextencrypt[4 * i + 2] ^ Mult2(plaintextencrypt[4 * i + 3]);
}
/* 逆列混淆 */
#define xtime(x) ((x<<1) ^ (((x>>7) & 1) * 0x1b))
#define Multiply(x,y) (((y & 1) * x) ^ ((y>>1 & 1) * xtime(x)) ^ ((y>>2 & 1) * xtime(xtime(x))) ^ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))
void MixColumnsRe(unsigned char *state)
{

unsigned char a, b, c, d;
for (int i = 0; i < 4; i++)
{
a = state[4*i];
b = state[4*i+1];
c = state[4*i+2];
d = state[4*i+3];
state[4 * i] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
state[4 * i + 1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
state[4 * i + 2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
state[4 * i + 3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
int CharToWord(unsigned char *character, int first)/* 字节转字 */
{
return (((int)character[first] & 0x000000ff) << 24) | (((int)character[first + 1] & 0x000000ff) << 16) | (((int)character[first + 2] & 0x000000ff) << 8) | ((int)character[first + 3] & 0x000000ff);
}
void WordToChar(unsigned int word, unsigned char *character)/* 字转字节 */
{
for (int i = 0; i < 4; character[i++] = (word >> (8 * (3 - i))) & 0xFF);
}
void ExtendCipherKey(unsigned int *CipherKey_word, int round)/* 密钥扩展 */
{
unsigned char CipherKeyChar[4] = { 0 },CipherKeyCharEncrypt[4] = { 0 };
unsigned int Rcon[10] = { 0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,0x40000000,0x80000000,0x1B000000,0x36000000 }; // 轮常量
for (int i = 4; i < 8; i++)
{
if (!(i % 4))
{
WordToChar((CipherKey_word[i - 1] >> 24) | (CipherKey_word[i - 1] << 8), CipherKeyChar);
SubBytes(CipherKeyChar, CipherKeyCharEncrypt, 4);
CipherKey_word[i] = CipherKey_word[i - 4] ^ CharToWord(CipherKeyCharEncrypt, 0) ^ Rcon[round];
}
else
CipherKey_word[i] = CipherKey_word[i - 4] ^ CipherKey_word[i - 1];
}
}
#include <stdlib.h>
void genString(int size){
char *text = (char *)malloc(size * sizeof(char));
for (size_t i = 0; i < size; i++)
{
text[i]=35+i;
}
puts(text);

}
char table[]="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
char res[]={0x36,0x35,0x34,0x33,0x3a,0x39,0x38,0x37,0x3e,0x3d,0x85,0x84,0x83,0x8a,0x89,0x88,0x87,0x8e,0x8d,0x8c,0x8b,0x92,0x91,0x90,0x8f,0x76,0x75,0x74,0x73,0x7a,0x79,0x78,0x77,0x7e,0x7d,0x7c,0xa5,0xa4,0xa3,0xaa,0xa9,0xa8,0xa7,0xae,0xad,0xac,0xab,0xb2,
0xb1,0xb0,0xaf,0x96,0x95,0x94,0x93,0x9a,0x99,0x98,0x97,0x9e,0x9d,0x9c,0x45,0x44,0x43,0x4a,0x49,0x48,0x47,0x4e,0x4d,0x4c,0x4b,0x52,0x51,0x50,0x4f,0x3c,0x3b,0x42,0x41,0x40,0x3f,0xa6,0x9b,0xa2,0xa1,0xa0,0x9f,0x86,0x7b,0x82,0x81,0x80,0x66,0x66
};
char getcc(char in){
for (size_t i = 0; i < 96; i++)
{
if (in==res[i])
{
return table[i];
}

}
}
void main()
{
printf("**************AES加解密***************\n");
int i = 0, k;
unsigned mm[]={0x71,0x55,0x7f,0xa8,0xfa,0x0e,0xa3,0x19,0xa0,0x5c,0xf9,0x0e,0x9b,0x0b,0x5e,0xfc,0xb5,0xa8,0x49,0xfd,0x90,0x99,0x74,0xc7,0x77,0x02,0x6a,0xf5,0x9a,0x6a,0xba,0x7f,0xfb,0xe7,0x68,0xda,0x54,0xee,0xe8,0xbb,0x78,0x01,0xe7,0xbb,0xa2,0x95,0x95,0xfa};
unsigned char PlainText[48] ="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
,
CipherKey[16] = { 0x05,0x06,0x07,0x08,0x37,0x42,0x4d,0x58,0x63,0x00,0x0a,0x0c,0x0d,0x0e,0x0f,0x10 },
CipherKey1[16] = { 0x05,0x06,0x07,0x08,0x37,0x42,0x4d,0x58,0x63,0x00,0x0a,0x0c,0x0d,0x0e,0x0f,0x10},
PlainText1[48] = {
0x71, 0x55, 0x7F, 0xA8, 0xFA, 0x0E, 0xA3, 0x19, 0xA0, 0x5C, 0xF9, 0x0E, 0x9B, 0x0B, 0x5E, 0xFC,
0xB5, 0xA8, 0x49, 0xFD, 0x90, 0x99, 0x74, 0xC7, 0x77, 0x02, 0x6A, 0xF5, 0x9A, 0x6A, 0xBA, 0x7F,
0xFB, 0xE7, 0x68, 0xDA, 0x54, 0xEE, 0xE8, 0xBB, 0x78, 0x01, 0xE7, 0xBB, 0xA2, 0x95, 0x95, 0xFA

},
PlainText2[48] = { 0x71, 0x55, 0x7F, 0xA8, 0xFA, 0x0E, 0xA3, 0x19, 0xA0, 0x5C, 0xF9, 0x0E, 0x9B, 0x0B, 0x5E, 0xFC,
0xB5, 0xA8, 0x49, 0xFD, 0x90, 0x99, 0x74, 0xC7, 0x77, 0x02, 0x6A, 0xF5, 0x9A, 0x6A, 0xBA, 0x7F,
0xFB, 0xE7, 0x68, 0xDA, 0x54, 0xEE, 0xE8, 0xBB, 0x78, 0x01, 0xE7, 0xBB, 0xA2, 0x95, 0x95, 0xFA
};
unsigned int CipherKey_word[44] = { 0 };
for (i = 0; i < 4; CipherKey_word[i++] = CharToWord(CipherKey, 4 * i));
printf("密钥:");
for (k = 0; k < 16; k++) printf("%2X ", CipherKey[k]);
printf("\n明文:");
for (k = 0; k < 16; k++) printf("%02X ", PlainText[k]);
printf("\n**************开始加密****************");
AddRoundKey(PlainText, CipherKey);
for (i = 0; i < 9; i++)
{
SubBytes(PlainText, PlainText1, 16);/*S 盒置换 */
ShiftRows(PlainText1); /* 行移位 */
MixColumns(PlainText1, PlainText2); /* 列混淆 */
ExtendCipherKey(CipherKey_word + 4 * i, i);/* 子密钥生成 */
for (k = 0; k < 4; k++) WordToChar(CipherKey_word[k + 4 * (i + 1)], CipherKey + 4 * k);
for (k = 0; k < 16; k++) printf("%02X ", CipherKey[k]);
AddRoundKey(PlainText2, CipherKey);/* 轮密钥加 */
for (k = 0; k < 16; k++) PlainText[k] = PlainText2[k];
}

SubBytes(PlainText, PlainText1, 16);
ShiftRows(PlainText1);
ExtendCipherKey(CipherKey_word + 4 * i, i);
for (k = 0; k < 4;WordToChar(CipherKey_word[k + 4 * (i + 1)], CipherKey + 4 * k), k++);
AddRoundKey(PlainText1, CipherKey);
printf("\n\n最终AES加密后的密文为:");
for (i = 0; i < 16; i++) printf("%02X ", PlainText1[i]);

for (size_t i = 0; i < 16; i++)
{
PlainText1[i]=mm[i]; // 解密第二段第三段偏移 16,32 即可
}


printf("\n\n**************开始解密***************");
AddRoundKey(PlainText1, CipherKey);
for (i = 0; i < 9; i++)
{
SubBytesRe(PlainText1, PlainText, 16);/*S 盒置换 */
for (k = 0; k < 4; WordToChar(CipherKey_word[k + 40 - 4 * (i + 1)], CipherKey + 4 * k),k++);/* 子密钥生成 */
ShiftRowsRe(PlainText);/* 行移位逆 */
AddRoundKey(PlainText, CipherKey);/* 轮密钥加 */
MixColumnsRe(PlainText);/* 列混淆逆运算 */
for (k = 0; k < 16;PlainText1[k] = PlainText[k],k++);
}
printf("\n最后一次循环:");
ShiftRowsRe(PlainText);/* 行移位逆 */
SubBytesRe(PlainText, PlainText1, 16);/*S 盒置换 */
AddRoundKey(PlainText1, CipherKey1);
printf("\n最终AES解密后的明文为:");

//for (i = 0; i < 16; i++) printf("%c",getcc( PlainText1[i]));
for (i = 0; i < 16; i++) printf("%c",PlainText1[i]);
printf("\n");

}

DES

DES(Data Encryption Standard,数据加密标准)是一种经典的对称分组密码算法,由IBM公司基于Lucifer算法改进而来,并于1977年被美国国家标准局正式确定为联邦数据加密标准。DES采用64 bit分组长度进行数据加密,其密钥长度为64 bit,其中实际有效密钥长度为56 bit,剩余8 bit用于奇偶校验

由于DES的有效密钥空间仅为2的56次方,随着计算机计算能力不断提升,暴力破解攻击逐渐变得可行,DES的安全性难以满足现代密码应用需求。为了提高安全性,后来出现了3DES算法,通过多次执行DES加密扩大密钥空间,但由于其计算效率较低,同时仍然受到64 bit分组长度的限制,因此无法作为长期使用的加密标准

在实际应用过程中,DES采用固定64 bit分组进行加密。当待加密明文长度不足64 bit时,需要按照规定方式进行填充;当明文长度超过64 bit时,则需要将数据划分为多个64 bit的数据块,并按照指定工作模式进行分组加密

加密流程

DES内部采用经典的Feistel结构,将64 bit明文分为左右两个32 bit部分,通过16轮迭代完成加密。每轮通过扩展置换、密钥混合、S盒替换以及P盒置换实现数据的混淆和扩散

初始置换(IP置换)

将输入的64位明文块进行置换和重新排列,生成新的64位数据块

a1是输入

加密流程

初始置换完成后,明文被划分成了相同长度(32位)的左右两部分,记作L0,R0。接下来就会进行16个轮次的加密

单独一个轮次来看,右半部分R0会作为下一轮次的左半部分L1的输入。同时,R0会补位到48位和本轮次生成的48位K0输入到F轮函数中去,F(R0,K0)结果会和L0进行异或运算作为下一轮次右半部分R1的输入

  • f = P(S(E(R) ^ round_key)) # 轮函数:扩展、异或子密钥、S盒、P置换
  • new_R = L ^ f # 用旧左半部混合轮函数输出
  • new_L = R # 左右交换

R拓展

将32位R0右半部分进行扩展,得到一个48位的数据块。数据拓展也是根据一个固定置换表,红框中就是要补位的数据,补位数据从输入的32位数据块提取出来

异或子密钥

DES算法采用每轮子密钥生成的方式来增加密钥的复杂性和安全性,每轮子密钥都是由64位主密钥通过密钥调度算法生成48 bit子密钥

置换 PC-1:64位变为56位

C和D先循环左移2位,再循环左移1位

PC-2:56位变为48位

当前轮次的子密钥Ki(在上面56位的基础上删除8位得到48位)与拓展的48位Ri进行异或运算,运算结果会作为接下来S盒替换的输入

S盒替换

通过S盒引入非线性,将上一轮异或运算的48位结果映射到32位输出

DES共有8个S盒,48bit变成8组 × 6bit。6bit里首尾两位作为行标,中间四位作为列标,在不同的S盒对应一个小于16的数,再转化为4bit输出

P盒代换

P盒代换将S盒替换的32位输出作为输入,经过上述固定的表代换后即为最后F轮函数的结果

逆置换

经过16轮运算后,将左右两部分重新组合,并进行最终置换得到64 bit密文

解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pyDes import *
import base64

def des_decrypt(cipher_text: str, key: str) -> str:
if len(key) != 8:
raise ValueError("DES 密钥必须为 8 字节长度")
des_obj = des(key, mode=ECB, padmode=PAD_PKCS5)#PAD_NORMAL
encrypted_data = base64.b64decode(cipher_text)
decrypted_data = des_obj.decrypt(encrypted_data)
return decrypted_data.decode('utf-8')

def des_decrypt_cbc(cipher_text: str, key: str, iv: str) -> str:
if len(key) != 8 or len(iv) != 8:
raise ValueError("DES 密钥和 IV 都必须为 8 字节")

des_obj = des(key, CBC, iv, padmode=PAD_PKCS5)
encrypted_data = base64.b64decode(cipher_text)
decrypted_data = des_obj.decrypt(encrypted_data)
return decrypted_data.decode('utf-8')

if __name__ == "__main__":
cipher = "IK7yIRweea6zCwdbwzx6fA=="
key = "12345678"
plain = des_decrypt(cipher, key)
print( plain)
cipher1='lwnEJyjSZoxW6bWftUiN8w=='
iv='12345678'
plain1=des_decrypt_cbc(cipher1,key,iv)
print(plain1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/env python3

IP = [
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的逆置换表

FP = [
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
]

E = [
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
]#扩展置换表

P = [
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
]

S = [
[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]
]

PC1 = [
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
]

PC2 = [
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
]

SHIFTS = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]

# --- 辅助函数 ---
# 取 input 中从左数第 pos 位(1-indexed),input 有 in_bits 位
def get_bit(input, pos, in_bits):
return (input >> (in_bits - pos)) & 1

# 置换函数:根据 table 对 input 置换,table_len 为置换表长度,input 有 in_bits 位
def permute(input, table, table_len, in_bits):
output = 0
for pos in table:
output = (output << 1) | get_bit(input, pos, in_bits)
return output

# 对 value(bits 位宽)循环左移 shift 位
def left_rotate(value, shift, bits):
return ((value << shift) | (value >> (bits - shift))) & ((1 << bits) - 1)

# --- 生成 16 个轮密钥 ---
def generate_round_keys(key):
# 使用 PC1 将 64 位密钥置换为 56 位
permuted_key = permute(key, PC1, 56, 64)
# 分为左右各 28 位
C = (permuted_key >> 28) & 0x0FFFFFFF
D = permuted_key & 0x0FFFFFFF
round_keys = []
for shift in SHIFTS:
C = left_rotate(C, shift, 28)
D = left_rotate(D, shift, 28)
combined = (C << 28) | D # 56 位
rk = permute(combined, PC2, 48, 56)
round_keys.append(rk)
return round_keys

# --- DES f 函数 ---
def des_f(R, round_key):
# 扩展 R 为 48 位
expanded_R = permute(R, E, 48, 32)
x = expanded_R ^ round_key
output = 0
for i in range(8):
shift = 42 - 6 * i
six_bits = (x >> shift) & 0x3F
row = ((six_bits & 0x20) >> 4) | (six_bits & 0x01)
col = (six_bits >> 1) & 0x0F
s_val = S[i][row * 16 + col]
output = (output << 4) | s_val
f_result = permute(output, P, 32, 32)
return f_result

# --- DES 加密 ---
def des_encrypt_block(block, key):
round_keys = generate_round_keys(key)
permuted_block = permute(block, IP, 64, 64)
L = (permuted_block >> 32) & 0xFFFFFFFF
R = permuted_block & 0xFFFFFFFF
for rk in round_keys:
L, R = R, L ^ des_f(R, rk)
preoutput = (R << 32) | L
cipher = permute(preoutput, FP, 64, 64)
return cipher

# --- DES 解密(轮密钥逆序)---
def des_decrypt_block(block, key):
round_keys = generate_round_keys(key)
permuted_block = permute(block, IP, 64, 64)
L = (permuted_block >> 32) & 0xFFFFFFFF
R = permuted_block & 0xFFFFFFFF
for rk in reversed(round_keys):
L, R = R, L ^ des_f(R, rk)
preoutput = (R << 32) | L
plain = permute(preoutput, FP, 64, 64)
return plain

def main():
# 示例明文与密钥(64 位,使用十六进制表示)
plaintext = 0x0123456789ABCDEF
key = 0x133457799BBCDFF1
cipher = des_encrypt_block(plaintext, key)
decrypted = des_decrypt_block(cipher, key)
print("明文: %016X" % plaintext)
print("加密后: %016X" % cipher)
print("解密后: %016X" % decrypted)

if __name__ == "__main__":
main()

3DES

SM4

SM4是我国自主设计的一种分组对称密码算法,最初主要用于无线局域网产品中的数据加密,后来逐步发展成为我国商用密码体系中的重要基础算法之一

加密流程

SM4每一组明文长度和密钥长度均为128bit, 进行32轮迭代运算和一次反序变换,每轮通过异或、S盒替换和循环移位等操作完成数据的混淆与扩散

下图是SM4的加密流程图

v8 = X0,sub_140001ED0对应T,v8 = X0 ^ T(rk0 ^ X3 ^ X2 ^ X1)= X4

根据性质L(S[b3]<<24 | S[b2]<<16 | S[b1]<<8 | S[b0])

等价于TABLE[b3]^rol(TABLE[b2], 24)^rol(TABLE[b1], 16)^rol(TABLE[b0], 8)

下图为查表优化实现

异或

每一组明文分为 $X_0, X_1, X_2, X_3$,$i$ 从 0 到 31。每一轮 $X_i$ 暂不处理,将 $X_{i+1}$、$X_{i+2}$、$X_{i+3}$ 和轮密钥 $rk_i$ 异或,得到一个 32 bit 的数据 sbox_input

S盒替换

将 sbox_input 拆分成4个8bit数据,分别进行S盒替换,之后再将4个8bit输出合并成一个32bit的 sbox_output

循环移位

将刚才获得的 sbox_output 分别循环左移 2、10、18、24 位,得到四个 32 bit 的结果,记为 $y_2, y_{10}, y_{18}, y_{24}$。将移位的结果 $y_2, y_{10}, y_{18}, y_{24}$ 与盒变换输出 sbox_output 和 $X_i$ 异或,得到 $X_{i+4}$

密钥拓展

如图密钥拓展流程和加密流程类似

将初始密钥 $(MK_0, MK_1, MK_2, MK_3)$ 分别异或固定参数 $(FK_0, FK_1, FK_2, FK_3)$,得到用于循环的密钥 $(K_0, K_1, K_2, K_3)$

$K_i$ 暂时不做处理,将 $K_{i+1}$、$K_{i+2}$、$K_{i+3}$ 和固定参数 $CK_i$ 异或,得到一个 32 bit 的数据

解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# -*- coding: utf-8 -*-

import binascii
from gmssl import sm4

class SM4:

def __init__(self):
self.crypt_sm4 = sm4.CryptSM4() # 实例化

def str_to_hexStr(self , hex_str):
hex_data = hex_str.encode('utf-8')
str_bin = binascii.unhexlify(hex_data)
return str_bin.decode('utf-8')

def encryptSM4(self , encrypt_key , value):
crypt_sm4 = self.crypt_sm4
crypt_sm4.set_key(encrypt_key.encode() , sm4.SM4_ENCRYPT) # 设置密钥
date_str = str(value)
encrypt_value = crypt_sm4.crypt_ecb(date_str.encode()) # 开始加密。bytes类型
return encrypt_value.hex() # 返回十六进制值

def decryptSM4(self , decrypt_key , encrypt_value):
crypt_sm4 = self.crypt_sm4
crypt_sm4.set_key(decrypt_key.encode() , sm4.SM4_DECRYPT) # 设置密钥
decrypt_value = crypt_sm4.crypt_ecb(bytes.fromhex(encrypt_value)) # 开始解密。十六进制类型
return decrypt_value.decode()
# return self.str_to_hexStr(decrypt_value.hex())


if __name__ == '__main__':
key = "C3AE5873D08418DA"
strData = '''{"addr":"","regnCode":"320100","medinsName":"","medinsLvCode":"","medinsTypeCode":"","openElec":"","pageNum":6,"pageSize":100}'''
SM4 = SM4()
print("原字符:" , strData)
encData = SM4.encryptSM4(key , strData) # 加密后的数据,返回bytes类型
print("sm4加密结果:" , encData)

decData = SM4.decryptSM4(key , encData)
print("sm4解密结果:" , decData) # 解密后的数据
#!/usr/bin/env python3

# SM4 S-盒
SM4_Sbox = [
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7, 0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3, 0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a, 0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95, 0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba, 0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b, 0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2, 0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52, 0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5, 0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55, 0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60, 0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f, 0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f, 0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd, 0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e, 0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20, 0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
]

def ROTL(x, n):
return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))

# τ 变换:对 32 位字按字节用 S-盒替换
def sm4_tau(A):
return ((SM4_Sbox[(A >> 24) & 0xFF] << 24) |
(SM4_Sbox[(A >> 16) & 0xFF] << 16) |
(SM4_Sbox[(A >> 8) & 0xFF] << 8) |
(SM4_Sbox[A & 0xFF]))

# 线性变换 L,用于加解密
def sm4_L(B):
return B ^ ROTL(B,2) ^ ROTL(B,10) ^ ROTL(B,18) ^ ROTL(B,24)

# 线性变换 L',用于密钥扩展
def sm4_L_key(B):
return B ^ ROTL(B,13) ^ ROTL(B,23)

# 生成 32 个轮密钥,MK 为 4 个 32 位整数
def sm4_key_schedule(MK):
FK = [0xa3b1bac6,0x56aa3350,0x677d9197,0xb27022dc]
CK = [
0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
0x10171e25,0x2c333a41,0x484f565d,0x646b7279
]
K = [0]*36
rk = [0]*32
K[0] = MK[0] ^ FK[0]
K[1] = MK[1] ^ FK[1]
K[2] = MK[2] ^ FK[2]
K[3] = MK[3] ^ FK[3]
for i in range(32):
temp = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]
t = sm4_tau(temp)
L_prime = sm4_L_key(t)
K[i+4] = K[i] ^ L_prime
rk[i] = K[i+4]
return rk

# SM4 加密:对 128 位数据块(4 个 32 位整数)加密
def sm4_encrypt_block(input_block, rk):
X = list(input_block)
for i in range(32):
temp = X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i]
t = sm4_tau(temp)
L_val = sm4_L(t)
X.append(X[i] ^ L_val)
# 输出顺序反转
return [X[35], X[34], X[33], X[32]]

# SM4 解密:使用轮密钥逆序
def sm4_decrypt_block(input_block, rk):
X = list(input_block)
for i in range(32):
temp = X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[31 - i]
t = sm4_tau(temp)
L_val = sm4_L(t)
X.append(X[i] ^ L_val)
return [X[35], X[34], X[33], X[32]]

def main():
# 示例 128 位明文与密钥(4 个 32 位整数表示)
plaintext = [0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210]
key = [0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210]
rk = sm4_key_schedule(key)
ciphertext = sm4_encrypt_block(plaintext, rk)
decrypted = sm4_decrypt_block(ciphertext, rk)
print("明文: ", " ".join(f"{x:08X}" for x in plaintext))
print("加密后: ", " ".join(f"{x:08X}" for x in ciphertext))
print("解密后: ", " ".join(f"{x:08X}" for x in decrypted))

if __name__ == "__main__":
main()
  • 标题: Reverse常见算法
  • 作者: j1ya
  • 创建于 : 2025-01-09 11:38:13
  • 更新于 : 2026-10-03 22:01:40
  • 链接: https://j1ya-22.github.io/2025/01/09/Reverse常见算法/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论