AES
加密原理
AES 是一种分组对称加密算法,标准分组长度为 128 位(16 字节),密钥长度可以是 128、192 或 256 位。
核心流程
AES 的加密过程由多个“轮”(Round)组成。对于 AES-128,总共有 10 轮。除了初始轮和最后一轮略有不同,每一轮都包含以下四个步骤:
1.1 字节代换 (SubBytes)
这是 AES 唯一的非线性变换。每个字节通过一个固定的查表操作(S-Box)进行替换。 特征:如果你在程序里看到一个 256 字节的随机数组(通常以 0x63, 0x7c, 0x77... 开头),那大概率就是 AES 的 S 盒。
1.2 行移位 (ShiftRows)
将矩阵的每一行进行循环左移。第一行不变,第二行移 1 位,第三行移 2 位,第四行移 3 位。 目的: 打乱字节在列间的排列。
1.3 列混淆 (MixColumns)
利用有限域 GF(28) 上的矩阵乘法,对每一列进行线性变换。 注意:最后一轮(Round 10)没有这一步。
1.4 轮密钥加 (AddRoundKey)
将当前的中间状态与该轮的“轮密钥”进行异或 (XOR) 操作。这是加密逻辑真正依赖密钥的地方。
2. 关键:密钥扩展 (Key Expansion)
AES 不直接把你的原始密钥用到每一轮。它会通过一个算法,将原始密钥“拉长”成多个轮密钥(Round Keys)。
AES 一次只能加密 16 字节。如果明文很长,就需要按照某种规则重复调用 AES 核心函数。
ECB (Electronic Codebook) - 电子密码本模式
这是最简单的模式。直接把明文切成 16 字节的一块块,每一块独立加密。
逆向特征:代码里没有初始向量(IV),循环调用加密函数,且输入输出直接对应。
缺点:同样的明文块加密后密文完全相同。在 CTF 中,如果你看到密文有规律的重复,基本就是 ECB。
CBC (Cipher Block Chaining) - 密码分组链接模式
这是 CTF 中最常见的模式。每一块明文在加密前,先与前一块的密文进行 异或 (XOR)。
IV (Initialization Vector):第一块明文需要与一个随机的“初始向量”异或。
逆向特征:代码中会出现一个 16 字节的 IV,且在 Encrypt 函数执行前后有明显的异或操作。
其他模式 (CTR, GCM, CFB)
CTR (Counter):把 AES 当成流加密。加密的是一个计数器,然后把结果与明文异或。逆向点: 这种模式下加密和解密逻辑完全一样(都是异或)。
GCM:带有认证能力的模式。除了密文,还会产出一个 Tag 用于校验数据是否被篡改。
2. 填充方式 (Padding)
由于 AES 要求输入必须是 16 字节的整数倍,如果你的明文(比如 "Hello")不到 16 字节,就必须凑数。这就叫填充。
PKCS#7 (最标准、最常见)
原理:缺几个字节,就填几个“几”。
缺 1 字节:末尾填 01
缺 3 字节:末尾填 03 03 03
特例:如果明文刚好是 16 字节的倍数,必须再增加一个完整的 16 字节填充(全部填 10),否则解密时无法区分是原始数据还是填充。
Zero Padding
原理:末尾全部补 00。
逆向点:如果解密后的明文末尾有很多 \x00,通常就是这种。
ANSI X.923
原理:末尾补 00,但最后一个字节记录填充的总长度。
代码实现
S盒、密钥扩展、核心加密逻辑、ECB/CBC 模式选择以及 PKCS#7 填充
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
// --- [1. 识别的关键点] ---
const uint8_t sbox[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
};
const uint8_t Rcon[11] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
// --- [2. 核心数学运算] ---
#define xtime(x) ((x << 1) ^ (((x >> 7) & 1) * 0x1b))
void KeyExpansion(uint8_t *key, uint8_t *w) {
uint8_t temp[4];
memcpy(w, key, 16);
for (int i = 16; i < 176; i += 4) {
memcpy(temp, w + i - 4, 4);
if (i % 16 == 0) {
uint8_t t = temp[0];
temp[0] = sbox[temp[1]] ^ Rcon[i/16];
temp[1] = sbox[temp[2]];
temp[2] = sbox[temp[3]];
temp[3] = sbox[t];
}
for (int j = 0; j < 4; j++) w[i + j] = w[i - 16 + j] ^ temp[j];
}
}
// --- [3. AES 单块加密核心] ---
void aes_encrypt_block(uint8_t state[16], uint8_t *w) {
// AddRoundKey (Round 0)
for (int i = 0; i < 16; i++) state[i] ^= w[i];
for (int round = 1; round <= 10; round++) {
// 1. SubBytes
for (int i = 0; i < 16; i++) state[i] = sbox[state[i]];
// 2. ShiftRows
uint8_t t[16];
t[0]=state[0]; t[4]=state[4]; t[8]=state[8]; t[12]=state[12];
t[1]=state[5]; t[5]=state[9]; t[9]=state[13]; t[13]=state[1];
t[2]=state[10];t[6]=state[14];t[10]=state[2]; t[14]=state[6];
t[3]=state[15];t[7]=state[3]; t[11]=state[7]; t[15]=state[11];
memcpy(state, t, 16);
// 3. MixColumns (Round 1-9 only)
if (round < 10) {
for (int i = 0; i < 4; i++) {
uint8_t a = state[i*4], b = state[i*4+1], c = state[i*4+2], d = state[i*4+3];
state[i*4] = xtime(a^b) ^ b ^ c ^ d;
state[i*4+1] = xtime(b^c) ^ c ^ d ^ a;
state[i*4+2] = xtime(c^d) ^ d ^ a ^ b;
state[i*4+3] = xtime(d^a) ^ a ^ b ^ c;
}
}
// 4. AddRoundKey
for (int i = 0; i < 16; i++) state[i] ^= w[round * 16 + i];
}
}
// --- [4. 工作模式与填充] ---
// PKCS#7 填充
int pkcs7_pad(uint8_t *data, int len) {
uint8_t pad = 16 - (len % 16);
for (int i = 0; i < pad; i++) data[len + i] = pad;
return len + pad;
}
// ECB 模式加密
void aes_ecb_encrypt(uint8_t *input, int len, uint8_t *key, uint8_t *output) {
uint8_t w[176];
KeyExpansion(key, w);
for (int i = 0; i < len; i += 16) {
memcpy(output + i, input + i, 16);
aes_encrypt_block(output + i, w);
}
}
// CBC 模式加密
void aes_cbc_encrypt(uint8_t *input, int len, uint8_t *key, uint8_t *iv, uint8_t *output) {
uint8_t w[176], temp_iv[16];
KeyExpansion(key, w);
memcpy(temp_iv, iv, 16);
for (int i = 0; i < len; i += 16) {
for (int j = 0; j < 16; j++) output[i + j] = input[i + j] ^ temp_iv[j];
aes_encrypt_block(output + i, w);
memcpy(temp_iv, output + i, 16);
}
}
// --- [5. 主函数测试] ---
int main() {
uint8_t key[16] = "CTF_RE_STUDENT!!"; // 16字节Key
uint8_t iv[16] = "1234567887654321"; // 16字节IV
char plain[] = "Hello AES Mode!"; // 明文
uint8_t buffer[64];
memset(buffer, 0, 64);
memcpy(buffer, plain, strlen(plain));
// 1. 填充
int padded_len = pkcs7_pad(buffer, strlen(plain));
uint8_t encrypted[64];
// 2. CBC 加密
printf("Encrypted (CBC): ");
aes_cbc_encrypt(buffer, padded_len, key, iv, encrypted);
for (int i = 0; i < padded_len; i++) printf("%02X", encrypted[i]);
printf("\n");
// 3. ECB 加密 (无IV)
printf("Encrypted (ECB): ");
aes_ecb_encrypt(buffer, padded_len, key, encrypted);
for (int i = 0; i < padded_len; i++) printf("%02X", encrypted[i]);
printf("\n");
return 0;
}
评论