前言

打比赛的时候遇到了加解密脚本编写,所以想写一篇总结,正好把逆向常见的那几个加密算法也总结总结,虽然自己也不太想学密码,但是该来的跑不掉
本来这篇文章我想的标题是常见加密算法,但考虑到MD5是hash,base是编码,所以就给这篇文章起名为常见密码学算法

编码

把数据从一种格式转换为另一种格式,用于传输或存储,不需要密码

base64

Base64编码每3个字节为一个编码组,对应的24个二进制位,然后按照每6位进行分组,分为4组,计算每组的值通过映射Base64编码表获得对应的值
下述实例为base64.b64decode(Man)=TWFu
b032393761228828d783be5953f4c8a2

c代码实现

1
2
3
4
out[o++] = b64_table[(b0 >> 2) & 0x3F]; //取 b0 的高 6 位
out[o++] = b64_table[((b0 & 0x3) << 4) | ((b1 >> 4) & 0x0F)];//取 b0 的低 2 位 + b1 的高 4 位,拼成 6 位。
out[o++] = b64_table[((b1 & 0x0F) << 2) | ((b2 >> 6) & 0x03)];//取 b1 的低 4 位 + b2 的高 2 位。
out[o++] = b64_table[b2 & 0x3F];//取 b2 的低 6 位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#换码base64加密
import base64
table1 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/=' # 自定义Base64 编码表
table2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' # 标准Base64 编码表

def custom_base64_decode(data): #解密
std_b64 = ''.join(table2[table1.index(c)] if c in table1 else c for c in data) #简单的映射变换
return base64.b64decode(std_b64) #标准base解密
def custom_base64_encode(raw_bytes): #加密
std_b64 = base64.b64encode(raw_bytes).decode() #标准加密
return ''.join(table1[table2.index(c)] if c in table2 else c for c in std_b64)
encoded = 'pCNxpTIUejgMhkgQdiQOdjB3bjwTdPgJgzcSdyQRgPoNcz16c44Vh31Z'
flag = custom_base64_decode(encoded)
print(flag.decode('utf-8', errors='ignore'))
# 验证反向编码
print(custom_base64_encode(flag))

对称加密

块密码

TEA / XTEA / XXTEA

TEA(Tiny Encryption Algorithm,微型加密算法)是一种结构简单、速度快、实现容易的分组对称加密算法,曾广泛用于早期嵌入式设备、游戏加密、移动通信协议。

算法还是很简单的,就是v1和v0不断相互作为参数进行加密迭代,左脚踩右脚,解密就是倒着来一遍

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
#include <stdio.h>
#include <stdint.h>
// TEA 加密函数
void encrypt(uint32_t* v, const uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t sum = 0; //累加器
uint32_t delta = 0x9e3779b9; //黄金比例常数,用于打乱循环效果
for (int i = 0; i < 32; i++) { //设置32 轮加密,可修改
sum += delta;
v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); //通过v1和k和sum得到新的v0
v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);//通过新的v0和k和sum得到新的v1
}
v[0] = v0;
v[1] = v1;
}
// TEA 解密函数
void decrypt(uint32_t* v, const uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t sum = delta * 32; //初始sum为delta*加密轮数
for (int i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);//还原v1
v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);//还原v0
sum -= delta;
}
v[0] = v0;
v[1] = v1;
}
int main() {
uint32_t v[2] = {0x12345678, 0x9abcdef0}; //明文长度必须是 8 字节(64 位)的倍数,明文分组(64位 → 两个32位整数)
uint32_t k[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444}; //密钥(128位 → 四个32位整数)
printf("原文: %08X %08X\n", v[0], v[1]);
encrypt(v, k);
printf("加密: %08X %08X\n", v[0], v[1]);
decrypt(v, k);
printf("解密: %08X %08X\n", v[0], v[1]);
return 0;
}

XTEA 是 TEA 的扩展,也称做 TEAN,四个子密钥采取不正规的方式进行混合以阻止密钥表攻击
XTEA与TEA的不同点在for循环里

1
2
3
4
5
6
7
8
//加密
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);//XTEA 每轮的key选择由sum决定,密钥使用顺序更混乱;
sum += DELTA; //XTEA 通过把 sum 的更新时机放到两次数据运算之间
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
//解密
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= DELTA;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);

前面的xtea和tea,在加密前需要两两分组,然后组内多轮相交
XXTEA 每轮都会让每个块都和它的前后块交互:每个块同时受到上下邻块的影响 → 扩散非常彻底。并且XXTEA支持任意长度(n×32位)

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
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define DELTA 0x9e3779b9
// ----------------------------
// XXTEA 加密
// v: 数据块数组 (长度 n)
// n: 数据块数量(每块32位)
// k: 128位密钥数组(4个32位整数)
// ----------------------------
void xxtea_encrypt(uint32_t *v, int n, const uint32_t k[4]) {
if (n < 2) return; // 至少两个32位块才能加密
uint32_t sum = 0;
uint32_t rounds = 6 + 52/n; // 推荐轮数公式
uint32_t z = v[n-1], y, e, p;
while (rounds-- > 0) {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n-1; p++) {
y = v[p+1];
// 核心混合公式:
v[p] += (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));
z = v[p];
}
//最后一块没有参加循环,最后一块与第一块混合,保证循环
y = v[0];
v[n-1] += (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));
z = v[n-1];
}
}
// XXTEA 解密
void xxtea_decrypt(uint32_t *v, int n, const uint32_t k[4]) {
if (n < 2) return;
uint32_t rounds = 6 + 52/n;
uint32_t sum = rounds * DELTA;
uint32_t z, y = v[0], e, p;

while (sum != 0) {
e = (sum >> 2) & 3;
for (p = n-1; p > 0; p--) {
z = v[p-1];
v[p] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));
y = v[p];
}
z = v[n-1];
v[0] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(0 & 3) ^ e] ^ z));
y = v[0];
sum -= DELTA;
}
}

DES

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
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes

key = b'12345678' # DES 密钥长度必须 8 字节
data = b'hello world' # 11 字节明文

# DES 块大小 = 8 字节,填充到块大小倍数
data_padded = pad(data, 8)#默认使用 PKCS#5 填充
print("填充后的明文:", data_padded) # b'hello world\x05\x05\x05'

# ECB 模式
cipher_ecb = DES.new(key, DES.MODE_ECB)
encrypted_ecb = cipher_ecb.encrypt(data_padded)
print("DES-ECB 加密:", encrypted_ecb.hex())

decrypted_ecb_padded = cipher_ecb.decrypt(encrypted_ecb)
decrypted_ecb = unpad(decrypted_ecb_padded, 8)
print("DES-ECB 解密:", decrypted_ecb)

# CBC 模式
# 生成随机 IV(初始化向量)
iv = get_random_bytes(8) # DES 块大小 = 8 字节
cipher_cbc = DES.new(key, DES.MODE_CBC, iv)
encrypted_cbc = cipher_cbc.encrypt(data_padded)
print("DES-CBC 加密:", encrypted_cbc.hex())

cipher_cbc_dec = DES.new(key, DES.MODE_CBC, iv)
decrypted_cbc_padded = cipher_cbc_dec.decrypt(encrypted_cbc)
decrypted_cbc = unpad(decrypted_cbc_padded, 8)
print("DES-CBC 解密:", decrypted_cbc)

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
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes

key = b'1234567812345678' #加密密钥,AES-128要求密钥为16 字节
data = b'hello world' # 11 字节

# 填充到 16 的倍数,AES是块加密算法,每次只能加密固定长度的块:如果明文长度不是16字节的倍数,直接加密会报错或数据不完整
data_padded = pad(data, 16) #默认使用 PKCS#7 填充
print(data_padded) # b'hello world\x05\x05\x05\x05\x05'

#ecb模式
# 加密
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(data_padded)
print("AES-ECB 加密:", encrypted.hex())
# 解密
decrypted_padded = cipher.decrypt(encrypted)
decrypted = unpad(decrypted_padded, 16)# 去掉填充
print("AES-ECB 解密:", decrypted)

#CBC 模式
# 生成随机 IV(初始化向量)
iv = get_random_bytes(16) # AES 块大小 = 16 字节

# 加密
cipher_cbc = AES.new(key, AES.MODE_CBC, iv)
encrypted_cbc = cipher_cbc.encrypt(data_padded)
print("AES-CBC 加密:", encrypted_cbc.hex())
# 解密
cipher_cbc_dec = AES.new(key, AES.MODE_CBC, iv)
decrypted_cbc_padded = cipher_cbc_dec.decrypt(encrypted_cbc)
decrypted_cbc = unpad(decrypted_cbc_padded, 16) #
print("AES-CBC 解密:", decrypted_cbc)

流密码

RC4

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Cipher import ARC4

key = b'mysecretkey' # 确保你的key长度是16字节
cipher = ARC4.new(key) #创建一个 RC4(ARC4)流加密器
data = b'Hello, World!' # 要加密的数据
# 加密数据
encrypted_data = cipher.encrypt(data)
print("Encrypted:", encrypted_data)
# 解密时需要重新创建 cipher
cipher_dec = ARC4.new(key)
decrypted_data = cipher_dec.decrypt(encrypted_data)
print("Decrypted:", decrypted_data)
具体实现
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
#include <stdio.h>
#include <string.h>

// 初始化置换盒 S
void rc4_init(unsigned char *s, unsigned char *key, int keylen) {
int i, j = 0;
unsigned char temp;
for (i = 0; i < 256; i++) s[i] = i; // 初始化 S 为 0~255
for (i = 0; i < 256; i++) {
j = (j + s[i] + key[i % keylen]) % 256;// 通过密钥映射和累加打乱 S
// 交换 s[i] 和 s[j]
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
// 加密 / 解密(同一个函数)
void rc4_crypt(unsigned char *s, unsigned char *data, int datalen) {
int i = 0, j = 0, k, t;
unsigned char temp;
for (k = 0; k < datalen; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
// 交换 s[i] 和 s[j]
temp = s[i];
s[i] = s[j];
s[j] = temp;
t = (s[i] + s[j]) % 256;// 生成伪随机字节 t
data[k] ^= s[t];// 将伪随机字节与明文/密文异或,这就是为什么加解密是同一个函数
}
}

// 测试示例
int main() {
unsigned char key[] = "secret"; // 密钥
unsigned char data[] = "Hello, RC4!"; // 明文
unsigned char s[256]; // S 盒
rc4_init(s, key, strlen((char *)key)); // 初始化
rc4_crypt(s, data, strlen((char *)data)); // 加密
printf("加密后: ");
for (int i = 0; i < strlen((char *)data); i++)
printf("%02X ", data[i]);
printf("\n");

// 再次初始化并解密(RC4 对称)
rc4_init(s, key, strlen((char *)key));// 再次初始化
rc4_crypt(s, data, strlen((char *)data)); //使用相同的函数处理即可解密
printf("解密后: %s\n", data);

return 0;
}

非对称加密

RSA

原始rsa是纯数学加密,就是直接把明文通过数学公式变成密文,而没有额外随机性或结构。
现代rsa引入填充机制,在加密前加入随机掩码,解决纯数学加密的“同明文密文固定”问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

# 1️⃣ 生成 RSA 密钥对(2048 位)
key = RSA.generate(2048)
private_key = key.export_key() #导出私钥(PEM 格式)
public_key = key.publickey().export_key() #导出公钥(PEM 格式)
# PEM 格式是 Base64编码的二进制数据,通常以特定头尾包裹,如-----BEGIN RSA PRIVATE KEY----- 开头
print("私钥:\n", private_key.decode()[:100], "...") # 只显示前100字符
print("公钥:\n", public_key.decode()[:100], "...")

# 2️⃣ 公钥加密
message = b"Hello RSA!"
pub_key = RSA.import_key(public_key)# 导入公钥字符串
cipher_encrypt = PKCS1_OAEP.new(pub_key)#使用 PKCS#1 OAEP 填充方式
encrypted_data = cipher_encrypt.encrypt(message) #生成密文为二进制数据
print("加密后:", encrypted_data.hex()[:60], "...") #转换成16进制字符串显示前60字符

# 3️⃣ 私钥解密
priv_key = RSA.import_key(private_key)
cipher_decrypt = PKCS1_OAEP.new(priv_key)
decrypted_data = cipher_decrypt.decrypt(encrypted_data)
print("解密后:", decrypted_data.decode())

哈希函数

哈希(Hash,也叫散列或摘要)是一种单向映射函数:你能从明文得到哈希,不能从哈希反推出明文。
输入任意数据 → 输出固定长度字符串(通常是十六进制)。

文件的hash,就是将文件数据作为输入,计算出hash,这时只要修改文件,文件的hash值就会改变

MD5

MD5可以将任意长度的数据 → 映射为 128 位(16 字节)的固定长度哈希值,最后呈现结果是结果32位十六进制字符

1
2
3
4
5
6
7
8
import hashlib

s = "hello"
md5_hash = hashlib.md5(s.encode()).hexdigest()#encode 转为 UTF-8 编码的字节得到的 b'hello'
#.hexdigest() —— 哈希对象 → 十六进制字符串,编码结果是16字节的0x5d41402abc4b2a76b9719d911017c592,变成字符串5d41402abc4b2a76b9719d911017c592
# 相当于python的.hex()
print(md5_hash)#结果32位十六进制字符,
print(md5_hash[8:-8]) #16位MD5,取32位中间的16位,并不安全

SHA 系列

SHA 跟MD5相比输出长,更安全

1
2
3
4
5
6
7
8
9
10
11
import hashlib

text = "hello world"

sha1 = hashlib.sha1(text.encode()).hexdigest() #20字节
sha256 = hashlib.sha256(text.encode()).hexdigest() #32字节
sha512 = hashlib.sha512(text.encode()).hexdigest() #64字节

print("SHA1:", sha1)
print("SHA256:", sha256)
print("SHA512:", sha512)