AES对称加密算法

news2024/11/23 15:52:01

1. 简介


AES是一种对称加密算法, 它有3种类型:

  • AES-128: 密钥为128位(16字节)的AES, 加密10轮
  • AES-192: 密钥为192位(24字节)的AES, 加密12轮
  • AES-256: 密钥为256位(32字节)的AES, 加密14轮

密钥长度越长, 加密的强度越大, 当然与此同时开销也越大。每种类型下都有几种操作模式

  • ECB(Electronic Codebook): 每个数据块独立加密
  • CBC(Cipher Block Chaining): 每个数据块的加密依赖于前一个数据块的密文
  • CFB(Cipher Feedback): 将加密块的部分作为输入反馈给下一个块的加密过程
  • OFB(Output Feedback): 类似于CFB, 但加密反馈来自前一个块的输出, 不涉及密文本身
  • CTR(Counter Mode): 使用计数器为每个块生成唯一的值, 然后与明文块进行xor操作
  • GCM(Galois/Counter Mode): 提供加密和信息完整性校验, 适用于需要认证的加密场景, 比如网络通讯
  • XTS-AES: 常用于磁盘加密, 结合AES与密钥掩码来提供更强的数据保护
  • AES-CMAC: 用于生成消息验证码, 确保信息完整性
  • AES-XTS: 一种用于硬盘加密的模式, 旨在提供抗篡改的数据保护

2. 原理


    这里主要记录AES-128的ECB模式, 也是最基础的一种。AES-128加密一共要10轮, 每一轮进行加密的密钥称为轮密钥(Round Key), 由于需要一个用户提供的初始轮密钥, 所以实际上是11轮。

    每一把轮密钥是16字节(AES-128), 所以AES密钥的长度为(1 + 10) * 16 = 176字节
但实际上用户只需要提供一把16字节的密钥即可, AES-128提供了密钥拓展算法(Key Expansion Algorithm), 可以由初始轮密钥(16字节)生成其他轮密钥。由于AES-128要加密10轮, 所以初始轮密钥要生产其他10把轮密钥, 一共生成10 * 16 = 160字节的密钥, 总共176字节。

    AES-128进行加密解密的前提需要几样东西:

  1. 两个大小为16 * 16, 也就是总共有256个字节的S-Box矩阵与S-Box逆矩阵(AES定义的固定值)
  2. 两个大小为4 * 4, 也就是总共有16个字节的单位矩阵(AES定义的固定值)
  3. 一把用户提供的轮密钥, 一共16字节(用户自定义)
  4. 用户提供的密文(用户自定义)

    注意, AES-128 ECB是对称加密算法, 它一次最多只加密16个字节(密钥长度决定的), 如果你要加密超过16个字节的怎么办? 那就把要加密的内容分成一个个16字节的块, 分别进行加密(这是ECB的情况)。

    如果说最后一个块不足16个字节怎么办? 比如一共有38字节的内容要加密, 所以就是16+16+6, 这就是进行了3次加密, 可最后一个块是6字节不足16字节, 那就使用填充(padding):

  1. 零填充: 不足的部分全部补0(不建议使用, 解密时可能产生混淆)
  2. PKCS#7填充: 不足的部分全部补上你最后一个块存在的字节数, 比如你最后一个块是6字节, 那就剩下的10字节全部填6

    AES-128的一轮加密分为几个步骤:

  1. 字节代换(SubKeys): 将明文内容的每个字节作为索引, 从S-Box中找值
  2. 行位移(ShiftRows): 将16字节的明文内容分成4份, 每份4个字节, 分别循环左移0, 1, 2, 3字节
  3. 列混淆(MixColumns): 将行位移后生成的数组(4 * 4)与单元矩阵进行矩阵乘法
  4. 轮密钥加法(AddRoundKeys): 将轮密钥与列混淆生成的数组(4 * 4)进行异或操作

这里分别来详细解释下, 先解释下字节代换:

    AES-128的准备工作中, 包含了1个256字节的SBox和另一个256字节的逆RSBox, 这两个的关系就是:

y = SBox[x]
z = RSBox[y]
z == x

    将一个值作为索引放入SBox[x]能获得值y, 把y放入RSBox[y]能得到z, 而z的值是等于x的, 所以相当于把y放入RSBox[y]能得到x

    进行这个目的是为了混淆, 加密的时候我把明文x放入SBox[x]中获得了一个完全不相关的值y, 解密的时候我只要把y放入RSBox[y]就能获得明文x了

    这两个数组都是AES-128标准中定义好了的, 是固定的:

#define SBOX_SIZE (256)

BYTE bSBox[SBOX_SIZE] = {
	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 };


BYTE bInvSBox[SBOX_SIZE] = {
	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 };

再看一下, 如何利用字节代换(SubKeys)进行加密吧, 我这里一次加密了一个DWORD, 其实是4个字节, 原理是一样的。

DWORD SBoxExchange(PBYTE pbSBox, DWORD dwNum)
{
	PBYTE pbPtr = (PBYTE)&dwNum;

	for (int i = 0; i < 4; ++i)
	{
		*(pbPtr + i) = pbSBox[*(pbPtr + i)];
	}
	return(dwNum);
}

接下来是行位移(ShiftRows), 拿一张图直接说吧:

不难理解吧? 就是把16字节长的明文, 想象成一个4 * 4的数组, 并且是列优先的。然后每一行进行对应循环位移即可。

看一下代码实现:

// pState is a pointer which point to a 16 bytes array
VOID ShiftRows(BYTE* pState)
{
	BYTE bTmp = 0;

	// Row 2 move left 1 byte
	bTmp = *(pState + 1);
	*(pState + 1) = *(pState + 5);
	*(pState + 5) = *(pState + 9);
	*(pState + 9) = *(pState + 13);
	*(pState + 13) = bTmp;

	// Row 3 move left 2 bytes
	bTmp = *(pState + 2);
	*(pState + 2) = *(pState + 10);
	*(pState + 10) = bTmp;
	bTmp = *(pState + 6);
	*(pState + 6) = *(pState + 14);
	*(pState + 14) = bTmp;

	// Row 4 move left 3 bytes
	bTmp = *(pState + 3);
	*(pState + 3) = *(pState + 15);
	*(pState + 15) = *(pState + 11);
	*(pState + 11) = *(pState + 7);
	*(pState + 7) = bTmp;
}

其实它还有一种更优雅的写法, 是一个公式, 你可以选择用也可以不用:

// formula: state'[i][j] = state[i][(j + i) % 4] 其中i, j∈[0, 3]
VOID 
RowShift(__in PBYTE pbMatrix, 
    __out PBYTE pbReverseMatrix, 
    __in size_t nMatrixSize = 16)
{
    if (!pbMatrix || !pbReverseMatrix || !nMatrixSize || 16 != nMatrixSize)
    {
        return;
    }

    for (int i = 0; i < 4; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            pbReverseMatrix[i * 4 + j] = pbMatrix[4 * i + (4 + j - i) % 4];
        }
    }

    return;
}

说了正向的行位移(RowShifts), 还有逆向的。 正向是加密的时候用, 逆向是解密的时候用, 其实是完全一样的, 唯一的区别就是逆向位移是往右的。

看一下代码实现:

VOID InvShiftRow(BYTE *pState)
{
	if (!pState)
	{
		return;
	}
	BYTE bTmp = 0;
	// Row 1 rotate right by 1
	bTmp = pState[13];
	pState[13] = pState[9];
	pState[9] = pState[5];
	pState[5] = pState[1];
	pState[1] = bTmp;

	// Row 2 rotate right by 2
	bTmp = pState[14];
	pState[14] = pState[6];
	pState[6] = bTmp;
	bTmp = pState[10];
	pState[10] = pState[2];
	pState[2] = bTmp;
	
	// Row 3 rotate right by 3
	bTmp = pState[15];
	pState[15] = pState[3];
	pState[3] = pState[7];
	pState[7] = pState[11];
	pState[11] = bTmp;
}

同样它也有一个优雅实现的版本:

VOID ReverseRowShift(PBYTE pbReverseMatrix, PBYTE pbMatrix, size_t nMatrixSize = 16)
{
    if (!pbMatrix || !pbReverseMatrix || !nMatrixSize || 16 != nMatrixSize)
    {
        return;
    }

    for (int i = 0; i < 4; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            pbReverseMatrix[i * 4 + j] = pbMatrix[i * 4 + (4 + j + i) % 4];
        }
    }

    return;
}

接下来就是列混淆了(MixColumns), 它的操作实际上是让状态矩阵(其实就是16字节的明文数组被加密后的状态)与AES标准固定的一个4 * 4单位矩阵的进行矩阵乘法

用于加密的单元矩阵MixColumns:
| 02 03 01 01 |
| 01 02 03 01 |
| 01 01 02 03 |
| 03 01 01 02 |
用于解密的单元矩阵InvMixColumns:
| 0E 0B 0D 09 |
| 09 0E 0B 0D |
| 0D 09 0E 0B |
| 0B 0D 09 0E |

两者满足以下关系, 他们两者互逆, 你可以把它理解成1把锁和1把钥匙的关系, 两者角色可以对调。假设有一扇门(明文), 被其中之一上锁了(明文与单位矩阵相乘), 可以用钥匙解锁(密文与另一个单位矩阵相乘). 相反也成立的。

具体这里参考矩阵的乘法, 不做过多叙述。

BYTE mul2(BYTE x)
{
	return(((x >> 7) * 0x1B) ^ (x << 1));
}

BYTE mul03(BYTE x)
{
	// 0x03(3) = 2^1 ^ 1
	return(mul2(x) ^ x);
}

/*
* MixColumns
* [02 03 01 01]   [s0  s4  s8  s12]
* [01 02 03 01] . [s1  s5  s9  s13]
* [01 01 02 03]   [s2  s6  s10 s14]
* [03 01 01 02]   [s3  s7  s11 s15]
*/
VOID MixColumns(BYTE* pbState, BYTE* pOutput)
{
	for (int i = 0; i < 4; ++i)
	{
		BYTE s0 = pbState[i * 4];
		BYTE s1 = pbState[i * 4 + 1];
		BYTE s2 = pbState[i * 4 + 2];
		BYTE s3 = pbState[i * 4 + 3];

		pOutput[i * 4] = mul2(s0) ^ mul03(s1) ^ s2 ^ s3;
		pOutput[i * 4 + 1] = s0 ^ mul2(s1) ^ mul03(s2) ^ s3;
		pOutput[i * 4 + 2] = s0 ^ s1 ^ mul2(s2) ^ mul03(s3);
		pOutput[i * 4 + 3] = mul03(s0) ^ s1 ^ s2 ^ mul2(s3);
	}
}

这里来解释一下mul2是什么意思, 先来看一下它的另一种更易于阅读的写法:

BYTE mul2(BYTE x)
{
	return((x & 0x80) ? (0x1B ^ (x << 1)) : (x << 1));
}

BYTE是windows定义的类型, 其实际上是unsigned char, 是无符号字符型, 范围是从0~255, mul2本质上是做左移1位的操作, 就是乘以2

(x << 1)和(x * 2)是等价的

但左移时, 当最高位是1的时候就会产生溢出, AES规定所有计算都必须限制在GF(2^8)有限域中, 不用管这些复杂的数学名词, 大白话就是:

    如果最高位是1, 那就要对其异或上0x1B

所以这里(x & 0x80)其实就是检测最高位

如果是1那就((x << 1) ^ 0x1B), 如果是0, 那就直接(x << 1)即可。

总结下来就是, 对x乘2, 如果x最高为为1, 则溢出所以异或上0x1B

那这个mul3是什么?

BYTE mul03(BYTE x)
{
	// 0x03(3) = 2^1 ^ 1
	return(mul2(x) ^ x);
}

其实就是x乘以3, 并让它以2的多项式方式实现, (2^1 + 2^0) * x这样就可以了

在GF(2^8)有限域中所有加法都是xor异或操作。

所以就变成了2^1 * x ⊕ 1 * x, (2^1 * x)等价于(x << 1)

所以就是(mul2(x) ⊕ x)也就是(mul2(x) ^ x)了。其他所有的都是这样。

那个函数只是在模仿矩阵乘法的操作, 现在给出2个矩阵A为单位矩阵, B为状态矩阵

现在对其进行加密, 使用单位矩阵A, 乘以状态矩阵B, C0的第一列如下, 其他列也是用相同的方法进行计算。用A的第1行每个元素, 分别乘以B的第1列的每个元素, 并相加(在GF(2^8)有限域内加法都变成了异或(⊕))

同样, 既然有正向的列混淆,那也就有逆向的列混淆, 正向用于加密, 逆向用于解密。其实就是将矩阵换成其逆矩阵:

BYTE mul0e(BYTE x)
{
	// 0x0e(14) = 2^3 + 2^2 + 2^1
	return(mul2(mul2(mul2(x))) ^ mul2(mul2(x)) ^ mul2(x));
}

BYTE mul0b(BYTE x)
{
	// 0x0b(11) = 2^3 + 2^1 + 1
	return(mul2(mul2(mul2(x))) ^ mul2(x) ^ x);
}

BYTE mul0d(BYTE x)
{
	// 0x0d(13) = 2^3 + 2^2 + 1
	return(mul2(mul2(mul2(x))) ^ mul2(mul2(x)) ^ x);
}

BYTE mul09(BYTE x)
{
	// 0x09(9) = 2^3 + 1
	return(mul2(mul2(mul2(x))) ^ x);
}

BYTE mul2(BYTE x)
{
	return(((x >> 7) * 0x1B) ^ (x << 1));
}

/*
Inverse MixColumns
[0e 0b 0d 09]   [s0  s4  s8  s12]
[09 0e 0b 0d] . [s1  s5  s9  s13]
[0d 09 0e 0b]   [s2  s6  s10 s14]
[0b 0d 09 0e]   [s3  s7  s11 s15]
*/
VOID InvMixColumns(BYTE* pbState, BYTE *pOutput)
{

	for (int i = 0; i < 4; ++i)
	{
		BYTE s0 = pbState[i * 4];
		BYTE s1 = pbState[i * 4 + 1];
		BYTE s2 = pbState[i * 4 + 2];
		BYTE s3 = pbState[i * 4 + 3];

		pOutput[i * 4] = mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3);
		pOutput[i * 4 + 1] = mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3);
		pOutput[i * 4 + 2] = mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3);
		pOutput[i * 4 + 3] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3);
	}
}

他只是将单位矩阵换成了其逆矩阵:

下面是结果的第一列的计算方法, 其他列也是如此

最后就是轮密钥加法(AddRoundKeys)了, 这一步比较简单, 就是将上一步得到的状态矩阵与轮密钥进行异或操作:

// AddRoundKey
for (i = 0; i < AES_BLOCK_SIZE; ++i, ++pbRoundKey)
{
    *(pbCipherText + i) ^= *pbRoundKey;
}

3. 加密流程梳理


    加密的所有步骤都说完了, 接下来整体梳理一遍。

  1. 首先用户准备16字节的轮密钥, 外加明文K, 明文K不足16字节, 用PKCS#7填充
  2. 使用密钥拓展算法(Key Expansion)生成160个字节, 算上初始轮密钥一共11把轮密钥(176字节)
  3. 进入第1轮加密, 将明文K(16字节)与初始密钥(16字节)进行异或操作(AddRoundKeys), 得到的内容称为状态矩阵, 第一轮只有AddRoundKeys步骤
  4. 进入第2-10轮加密, 这里面包含了4步, 按照字节代换(SubKeys), 行位移(RowShifts), 列混淆(MixColumns), 加轮密钥(AddRoundKeys)这4步进行
  5. 进入第11轮加密, 这里按照字节代换(SubKeys), 行位移(RowShifts), 加轮密钥(AddRoundKeys)这3步进行, 注意这里没有列混淆(MixColumns)

4. 密钥拓展算法(Key Expansion Algorithm)


    这个算法的目的是从一个固定长度的初始密钥(种子密钥)中生成多个轮密钥, 以供AES加密的每一轮使用。对于AES-128, 其初始密钥长度是16字节, 但AES加密过程需要10把轮密钥, 所以这个算法负责通过初始密钥生成这10把密钥。

    初始密钥记为: k0, k1, k2, ..., k15, 一共16个字节, 128位。

    将这16个字节分成4个32位的DWORD, 每个w[i]包含4个字节, 例如: w[0] = k0 || k1 || k2 || k3

    这4个字节分别记作w[0], w[1], w[2], w[3]

    从w[4]开始, 根据上一个DWORD, 也就是w[3]和特定的规则生成新的32位字

    规则如下:

if (i % 4 == 0) w[i] = w[i - 4] ^ g(w[i - 1])

if (i % 4 != 0) w[i] = w[i - 4] ^ w[i - 1]

    如果看迷糊了没关系, 大白话来说一下, 一把轮密钥一共16字节, 分成4个4字节。记为w[0], w[1], w[2], w[3], 下面要新生成一把轮密钥(16字节), 首先要生成第一个DWORD, 也就是w[4], 这个i就是4, 发现其能被4整除, 所以w[4] = w[0] ^ g(w[3]), 推倒下去:

4 % 4 == 0索引能被4整除, 使用g()函数: w[4] = w[0] ^ g(w[3])

5 % 4 != 0索引不能被4整除: w[5] = w[1] ^ w[4]

6 % 4 != 0索引不能被4整除: w[6] = w[2] ^ w[5]

7 % 4 != 0索引不能被4整除: w[7] = w[3] ^ w[6]

    总结一下, 其永远是上一个DWORD与上一把轮密钥的同等位置的DWORD进行异或操作, 如果当前索引能被4整除, 就要用g()处理一下

    

结合上图看看, w[4]就是其上一个DWORD, 也就是w[3]和上一把轮密钥的同等位置w[0]两者异或生成的。如果当前正在生成的DWORD的索引能被4整除, 那需要为上一个DWORD加上g()处理

原理说完了, 先把代码贴出来:

// Round constants
const DWORD dwRoundConst[10] = {
	0x01000000, 0x02000000, 0x04000000, 0x08000000,
	0x10000000, 0x20000000, 0x40000000, 0x80000000,
	0x1B000000, 0x36000000
};

DWORD Ror8Bits(DWORD dwWord)
{
	return(((dwWord << 8) & 0xFFFFFF00) | ((dwWord >> 24) & 0x000000FF));
}

DWORD SBoxExchange(PBYTE pbSBox, DWORD dwNum)
{
	PBYTE pbPtr = (PBYTE)&dwNum;

	for (int i = 0; i < 4; ++i)
	{
		*(pbPtr + i) = pbSBox[*(pbPtr + i)];
	}
	return(dwNum);
}

// key is 128 bits => 16 bytes
// roundkey is 176 bytes (1 + 10) * 16 = 176 bytes
VOID KeyScheduleAlgo(PBYTE pbKey, PBYTE pbRoundKey)
{
	if (!pbKey || !pbRoundKey)
	{
		return;
	}
	PDWORD pdwLastWord = nullptr;
	PDWORD pdwFirstWord = nullptr;
	PDWORD pdwStartWord = nullptr;

	// No.0 key, also the initial key
	for (int i = 0; i < AES_BLOCK_SIZE; ++i)
	{
		pbRoundKey[i] = pbKey[i];
	}
	pdwLastWord = (((PDWORD)pbRoundKey) + 3); // w[3]
	pdwFirstWord = (PDWORD)pbRoundKey; // w[0]
	pdwStartWord = pdwLastWord + 1; 
	// it's 10 rounds
	DWORD dwRor8 = 0;
	for (int i = 0; i < AES_TOTAL_ROUNDS; ++i)
	{
		dwRor8 = Ror8Bits(*pdwLastWord);
		dwRor8 = SBoxExchange(bSBox, dwRor8);
		dwRor8 ^= dwRoundConst[i];
		*pdwStartWord = dwRor8 ^ *pdwFirstWord;
		++pdwLastWord; // w[4]
		++pdwFirstWord; // w[1]
		++pdwStartWord;
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; // w[5]
		++pdwFirstWord; // w[2]
		++pdwStartWord; 
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; // w[6]
		++pdwFirstWord; // w[3]
		++pdwStartWord;
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; 
		++pdwFirstWord; 
		++pdwStartWord;
	}
}

下面仔细记录一下这个g()函数, 他是专门处理索引能被4整除的索引对应的DWORD的。那它具体做了什么呢?

  1. 首先他将w[i-1]保存到一个临时的DWORD中, w[i-1]也就是要生成DWORD的上一个DWORD。比如说是wt, 将wt循环左移了1字节, 假设wt=0x11223344, 那他就将wt变成了0x22334411
  2. 接着wt实际上有4个字节, 分别对其每个字节进行字节代换(SubBytes)操作。
  3. 最后就是使用对应轮数的轮常量对wt进行异或操作, 由于要生成10把轮密钥, 目前生成的是第1把, 所以这里应该使用的是索引为0的轮常量。到此g()函数所有的任务完成了

完成g函数的工作, w[i-1]就被处理好了, 接下来就能使用上一把轮密钥的对应DWORD也就是w[i-4]与其进行xor, 只有索引与4的倍数是特殊的, 其他不需要经过g()函数的处理, 让w[i-1]直接与w[i-4]异或即可

完整的代码贴一下:

#include <windows.h>
#include <cstdio>
#include <cstdlib>
#define AES_BLOCK_SIZE (16)
#define AES_TOTAL_ROUNDS (10)

#define SBOX_SIZE (256)

BYTE bSBox[SBOX_SIZE] = {
	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 };


BYTE bInvSBox[SBOX_SIZE] = {
	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 };


const DWORD dwRoundConst[AES_TOTAL_ROUNDS] = {
	0x01000000, 0x02000000, 0x04000000, 0x08000000,
	0x10000000, 0x20000000, 0x40000000, 0x80000000,
	0x1B000000, 0x36000000
};

DWORD Ror8Bits(DWORD dwWord)
{
	return(((dwWord << 8) & 0xFFFFFF00) | ((dwWord >> 24) & 0x000000FF));
}

DWORD SBoxExchange(PBYTE pbSBox, DWORD dwNum)
{
	PBYTE pbPtr = (PBYTE)&dwNum;

	for (int i = 0; i < 4; ++i)
	{
		*(pbPtr + i) = pbSBox[*(pbPtr + i)];
	}
	return(dwNum);
}

// key is 128 bits => 16 bytes
// roundkey is 176 bytes (1 + 10) * 16 = 176 bytes
VOID KeyScheduleAlgo(PBYTE pbKey, PBYTE pbRoundKey)
{
	if (!pbKey || !pbRoundKey)
	{
		return;
	}
	PDWORD pdwLastWord = nullptr;
	PDWORD pdwFirstWord = nullptr;
	PDWORD pdwStartWord = nullptr;

	// No.0 key, also the initial key
	for (int i = 0; i < AES_BLOCK_SIZE; ++i)
	{
		pbRoundKey[i] = pbKey[i];
	}
	pdwLastWord = (((PDWORD)pbRoundKey) + 3); // w[3]
	pdwFirstWord = (PDWORD)pbRoundKey; // w[0]
	pdwStartWord = pdwLastWord + 1; 
	// it's 10 rounds
	DWORD dwRor8 = 0;
	for (int i = 0; i < AES_TOTAL_ROUNDS; ++i)
	{
		dwRor8 = Ror8Bits(*pdwLastWord);
		dwRor8 = SBoxExchange(bSBox, dwRor8);
		dwRor8 ^= dwRoundConst[i];
		*pdwStartWord = dwRor8 ^ *pdwFirstWord;
		++pdwLastWord; // w[4]
		++pdwFirstWord; // w[1]
		++pdwStartWord;
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; // w[5]
		++pdwFirstWord; // w[2]
		++pdwStartWord; 
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; // w[6]
		++pdwFirstWord; // w[3]
		++pdwStartWord;
		*pdwStartWord = *pdwFirstWord ^ *pdwLastWord;
		++pdwLastWord; 
		++pdwFirstWord; 
		++pdwStartWord;
	}
}

VOID ShiftRows(BYTE* pState)
{
	BYTE bTmp = 0;

	// Row 2 move left 1 byte
	bTmp = *(pState + 1);
	*(pState + 1) = *(pState + 5);
	*(pState + 5) = *(pState + 9);
	*(pState + 9) = *(pState + 13);
	*(pState + 13) = bTmp;

	// Row 3 move left 2 bytes
	bTmp = *(pState + 2);
	*(pState + 2) = *(pState + 10);
	*(pState + 10) = bTmp;
	bTmp = *(pState + 6);
	*(pState + 6) = *(pState + 14);
	*(pState + 14) = bTmp;

	// Row 4 move left 3 bytes
	bTmp = *(pState + 3);
	*(pState + 3) = *(pState + 15);
	*(pState + 15) = *(pState + 11);
	*(pState + 11) = *(pState + 7);
	*(pState + 7) = bTmp;
}



VOID InvShiftRow(BYTE *pState)
{
	if (!pState)
	{
		return;
	}
	BYTE bTmp = 0;
	// Row 1 rotate right by 1
	bTmp = pState[13];
	pState[13] = pState[9];
	pState[9] = pState[5];
	pState[5] = pState[1];
	pState[1] = bTmp;

	// Row 2 rotate right by 2
	bTmp = pState[14];
	pState[14] = pState[6];
	pState[6] = bTmp;
	bTmp = pState[10];
	pState[10] = pState[2];
	pState[2] = bTmp;
	
	// Row 3 rotate right by 3
	bTmp = pState[15];
	pState[15] = pState[3];
	pState[3] = pState[7];
	pState[7] = pState[11];
	pState[11] = bTmp;
}

BYTE mul2(BYTE x)
{
	return(((x >> 7) * 0x1B) ^ (x << 1));
}

BYTE mul0e(BYTE x)
{
	// 0x0e(14) = 2^3 + 2^2 + 2^1
	return(mul2(mul2(mul2(x))) ^ mul2(mul2(x)) ^ mul2(x));
}

BYTE mul0b(BYTE x)
{
	// 0x0b(11) = 2^3 + 2^1 + 1
	return(mul2(mul2(mul2(x))) ^ mul2(x) ^ x);
}

BYTE mul0d(BYTE x)
{
	// 0x0d(13) = 2^3 + 2^2 + 1
	return(mul2(mul2(mul2(x))) ^ mul2(mul2(x)) ^ x);
}

BYTE mul09(BYTE x)
{
	// 0x09(9) = 2^3 + 1
	return(mul2(mul2(mul2(x))) ^ x);
}

/*
Inverse MixColumns
[0e 0b 0d 09]   [s0  s4  s8  s12]
[09 0e 0b 0d] . [s1  s5  s9  s13]
[0d 09 0e 0b]   [s2  s6  s10 s14]
[0b 0d 09 0e]   [s3  s7  s11 s15]
*/
VOID InvMixColumns(BYTE* pbState, BYTE *pOutput)
{

	for (int i = 0; i < 4; ++i)
	{
		BYTE s0 = pbState[i * 4];
		BYTE s1 = pbState[i * 4 + 1];
		BYTE s2 = pbState[i * 4 + 2];
		BYTE s3 = pbState[i * 4 + 3];

		pOutput[i * 4] = mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3);
		pOutput[i * 4 + 1] = mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3);
		pOutput[i * 4 + 2] = mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3);
		pOutput[i * 4 + 3] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3);
	}
}


BYTE mul03(BYTE x)
{
	// 0x03(3) = 2^1 ^ 1
	return(mul2(x) ^ x);
}

/*
* MixColumns
* [02 03 01 01]   [s0  s4  s8  s12]
* [01 02 03 01] . [s1  s5  s9  s13]
* [01 01 02 03]   [s2  s6  s10 s14]
* [03 01 01 02]   [s3  s7  s11 s15]
*/
VOID MixColumns(BYTE* pbState, BYTE* pOutput)
{
	for (int i = 0; i < 4; ++i)
	{
		BYTE s0 = pbState[i * 4];
		BYTE s1 = pbState[i * 4 + 1];
		BYTE s2 = pbState[i * 4 + 2];
		BYTE s3 = pbState[i * 4 + 3];

		pOutput[i * 4] = mul2(s0) ^ mul03(s1) ^ s2 ^ s3;
		pOutput[i * 4 + 1] = s0 ^ mul2(s1) ^ mul03(s2) ^ s3;
		pOutput[i * 4 + 2] = s0 ^ s1 ^ mul2(s2) ^ mul03(s3);
		pOutput[i * 4 + 3] = mul03(s0) ^ s1 ^ s2 ^ mul2(s3);
	}
}

VOID AES128Decrypt(const BYTE* pbRoundKey,
	const BYTE* pbCipherText,
	BYTE* pbPlainText)
{
	BYTE i = 0, j = 0;
	BYTE t, u, v;
	BYTE tmp[AES_BLOCK_SIZE] = { 0 };
	// 176 bytes totally, pointer to the begining of last 16 bytes
	pbRoundKey += 160;

	// first round
	for (i = 0; i < AES_BLOCK_SIZE; ++i)
	{
		*(pbPlainText + i) = *(pbCipherText + i) ^ *(pbRoundKey + i);
	}
	pbRoundKey -= 16;
	// Inverse ShiftRows
	InvShiftRow(pbPlainText);
	// Inverse SubBytes
	for (i = 0; i < AES_BLOCK_SIZE; ++i)
	{
		*(pbPlainText + i) = bInvSBox[*(pbPlainText + i)];
	}
	
	// 9 rounds
	for (j = 1; j < AES_TOTAL_ROUNDS; ++j)
	{
		for (i = 0; i < AES_BLOCK_SIZE; ++i)
		{
			*(tmp + i) = *(pbPlainText + i) ^ *(pbRoundKey + i);
		}

		/*
		 * Inverse MixColumns
		 * [0e 0b 0d 09]   [s0  s4  s8  s12]
		 * [09 0e 0b 0d] . [s1  s5  s9  s13]
		 * [0d 09 0e 0b]   [s2  s6  s10 s14]
		 * [0b 0d 09 0e]   [s3  s7  s11 s15]
		 */
		InvMixColumns(tmp, pbPlainText);

		// Inverse ShiftRows
		InvShiftRow(pbPlainText);

		// Inverse SubBytes
		for (i = 0; i < AES_BLOCK_SIZE; ++i) 
		{
			*(pbPlainText + i) = bInvSBox[*(pbPlainText + i)];
		}

		pbRoundKey -= 16;
	}

	// last AddRoundKey
	for (i = 0; i < AES_BLOCK_SIZE; ++i) 
	{
		*(pbPlainText + i) ^= *(pbRoundKey + i);
	}
}


VOID AES128Encrypt(const BYTE* pbRoundKey,
	const BYTE* pbPlainText,
	BYTE* pbCipherText)
{
	int i = 0, j = 0;
	BYTE bTmp[AES_BLOCK_SIZE] = { 0 };
	BYTE bMix = 0;

	// first AddRoundKey
	for (i = 0; i < AES_BLOCK_SIZE; ++i, ++pbRoundKey)
	{
		*(pbCipherText + i) = *(pbPlainText + i) ^ *pbRoundKey;
	}

	for (j = 1; j < AES_TOTAL_ROUNDS; ++j)
	{
		// SubBytes
		for (i = 0; i < AES_BLOCK_SIZE; ++i)
		{
			*(bTmp + i) = bSBox[*(pbCipherText + i)];
		}
		// ShiftRows
		ShiftRows(bTmp);
		// MixColumns
		// | 02 03 01 01 |
		// | 01 02 03 01 | * Status matrix
		// | 01 01 02 03 |
		// | 03 01 01 02 |
		MixColumns(bTmp, pbCipherText);
		// AddRoundKey
		for (i = 0; i < AES_BLOCK_SIZE; ++i, ++pbRoundKey)
		{
			*(pbCipherText + i) ^= *pbRoundKey;
		}
	}
	// last round
	for (i = 0; i < AES_BLOCK_SIZE; ++i)
	{
		*(pbCipherText + i) = bSBox[*(pbCipherText + i)];
	}
	ShiftRows(pbCipherText);
	for (i = 0; i < AES_BLOCK_SIZE; ++i, ++pbRoundKey)
	{
		*(pbCipherText + i) ^= *pbRoundKey;
	}
}

PBYTE PKCSPadding(PBYTE pbContent, DWORD dwContentLen, PDWORD pdwPaddingLen)
{
	if (!pbContent || !dwContentLen)
	{
		return(nullptr);
	}

	DWORD dwPadddngLen = (dwContentLen + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
	DWORD dwLeft = AES_BLOCK_SIZE - (dwPadddngLen - dwContentLen);

	if (pdwPaddingLen)
	{
		*pdwPaddingLen = dwPadddngLen;
	}
	// no matter what, we allocate memory for old buffer
	PBYTE pbNewBuf = (PBYTE)::malloc(dwPadddngLen);
	if (!pbNewBuf)
	{
		return(nullptr);
	}
	::memset(pbNewBuf, 0, dwPadddngLen);
	::memcpy_s(pbNewBuf, dwPadddngLen, pbContent, dwContentLen);

	if (!(dwContentLen % AES_BLOCK_SIZE))
	{
		// if the content length is a multiple of 16
		// no need to padding
		return(pbNewBuf);
	}

	for (DWORD dwIdx = dwContentLen; dwIdx < dwPadddngLen; ++dwIdx)
	{
		pbNewBuf[dwIdx] = dwLeft;
	}

	return(pbNewBuf);
}

BOOL AES128Enc(PBYTE pbBuf, DWORD dwBufLen, PBYTE pbScheduleKey)
{
	if (!pbBuf || !dwBufLen || !pbScheduleKey)
	{
		return(FALSE);
	}
	BYTE bCipherBuf[AES_BLOCK_SIZE];
	DWORD dwBufCnt = dwBufLen / AES_BLOCK_SIZE;
	for (int i = 0; i < dwBufCnt; ++i)
	{
		AES128Encrypt(pbScheduleKey, (pbBuf + i * AES_BLOCK_SIZE), bCipherBuf);
		::memcpy_s(pbBuf + i * AES_BLOCK_SIZE, AES_BLOCK_SIZE, bCipherBuf, AES_BLOCK_SIZE);
	}
	return(TRUE);
}

BOOL AES128Dec(PBYTE pbBuf, DWORD dwBufLen, PBYTE pbScheduleKey)
{
	if (!pbBuf || !dwBufLen || !pbScheduleKey)
	{
		return(FALSE);
	}
	BYTE bPlainBuf[AES_BLOCK_SIZE];
	DWORD dwBufCnt = dwBufLen / AES_BLOCK_SIZE;
	for (int i = 0; i < dwBufCnt; ++i)
	{
		AES128Decrypt(pbScheduleKey, (pbBuf + i * AES_BLOCK_SIZE), bPlainBuf);
		::memcpy_s(pbBuf + i * AES_BLOCK_SIZE, AES_BLOCK_SIZE, bPlainBuf, AES_BLOCK_SIZE);
	}
	return(TRUE);
}

VOID PrintHex(PBYTE pbContent, DWORD dwContentLen)
{
	for (int i = 0; i < dwContentLen; ++i)
	{
		printf("0x%02X ", pbContent[i]);
	}
}

int main()
{
	BYTE bScheduleKey[176] = { 0 };
	BYTE bInitialKey[16] = { 0x19, 0x84, 0x5C, 0xEB, 0x72, 0x90, 0x4F, 
		0x36, 0xDA, 0xC2, 0x0D, 0x7B, 0xA8, 0x53, 0xE6, 0x31 };
	CHAR szContent[] = "Do you wanna use AES-128 to encrypt your traffic? Very good to encrypt";
	BYTE bEncryptOutput[MAXBYTE] = { 0 };
	CHAR szDecrypted[MAXBYTE] = { 0 };
	DWORD dwPaddingLen = 0;

	PBYTE pbNewBuf = PKCSPadding((PBYTE)szContent, sizeof(szContent), &dwPaddingLen);
	if (!pbNewBuf)
	{
		return(-1);
	}
	// Generate round keys
	KeyScheduleAlgo(bInitialKey, bScheduleKey);
	printf("Key: \r\n");
	PrintHex(bScheduleKey, sizeof(bScheduleKey));
	printf("\r\n\r\n");

	// Encrypt plain text
	AES128Enc((PBYTE)pbNewBuf, dwPaddingLen, bScheduleKey);
	printf("Encrypted: \r\n");
	PrintHex(pbNewBuf, sizeof(szContent));
	printf("\r\n\r\n");

	// Decrypt cipher text
	AES128Dec(pbNewBuf, dwPaddingLen, bScheduleKey);
	printf("Decrypted: \r\n");
	printf("%s\r\n", pbNewBuf);

	free(pbNewBuf);
	
	system("pause");

	return(0);
}

(完)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2076838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE】深入浅出 Spring AOP:概念、实现与原理解析

目录 Spring AOPAOP概述Spring AOP快速⼊⻔引⼊AOP依赖编写AOP程序 Spring AOP 详解Spring AOP核⼼概念切点(Pointcut)连接点(Join Point)通知(Advice)切⾯(Aspect) 通知类型PointCut切⾯优先级 Order切点表达式execution表达式annotation⾃定义注解 MyAspect切⾯类添加⾃定义注…

力扣第71题:简化路径 放弃栈模拟,选择数据流√(C++)

目录 题目 思路 解题过程 复杂度 Code 题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff…

K8S持久化存储数据volumeMountsvolumes

环境&#xff1a; Ubuntu-1:192.168.114.110作为主 Ubuntu-2:192.168.114.120作为从1&#xff0c;node节点1 Ubuntu-3:192.168.114.130作为从2&#xff0c;node节点2 持久化volumeMounts pod里面&#xff1a;emptyDir和hostPath。存储在node&#xff0c;NFS...&#xff0c;Clo…

文本处理函数

1.文本的提取 left mid right 2.文本的查找与替换 replace&#xff0c;substitute 3.字符个数 len字符 lenb字节, office365好像没有此功能 4.数据的清理 clean , trim 5.找不同 exact

codetop标签动态规划大全C++讲解(上)!!动态规划刷穿地心!!学吐了家人们o(╥﹏╥)o

主要供自己回顾学习&#xff0c;会持续更新&#xff0c;题源codetop动态规划近半年 1.零钱兑换2.零钱兑换II3.面试题08.11.硬币4.单词拆分5.最长递增子序列6.最长递增子序列的个数7.得到山形数组的最少删除次数8.最长公共子序列9.最长重复子数组10.最长等差数列11.最大子数组和…

智能优化算法-海鸥优化算法(SOA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍&#xff1a; 海鸥优化算法 (Seagull Optimization Algorithm, SOA) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了海鸥的觅食、飞行和社会交互行为&#xff0c;用于解决复杂的优化问题。 SOA的工…

wxpython Scintilla styledtextctrl滚动条拖到头文本内容还有很多的问题

wxpython Scintilla styledtextctrl滚动条拖到头文本内容还有很多的问题 使用wxpython Scintilla styledtextctrl&#xff0c;滚动条不自动更新 滚动条拖到头文本内容还有很多&#xff0c;如下&#xff1a; 以下是拖到最后的状态&#xff1a; 明显看出下图的滚动条的格子比…

书生.浦江大模型实战训练营——(十一)LMDeploy 量化部署进阶实践

最近在学习书生.浦江大模型实战训练营&#xff0c;所有课程都免费&#xff0c;以关卡的形式学习&#xff0c;也比较有意思&#xff0c;提供免费的算力实战&#xff0c;真的很不错&#xff08;无广&#xff09;&#xff01;欢迎大家一起学习&#xff0c;打开LLM探索大门&#xf…

超声波清洗机哪个品牌好?专业测评师推荐四款高质量眼镜清洗机

近年来&#xff0c;越来越多的用户在使用超声波清洗机清洗小物件&#xff0c;因为超声波清洗机不仅能清洗眼镜&#xff0c;还能清洗各种各样的小饰品、餐具、茶具、剃须刀、金属制品等&#xff0c;有一个智能超声波清洗机在家里&#xff0c;对于生活质感的提升还是挺大的&#…

第一个NIO开发演示

文章目录 Channel简介Buffer简介第一个NIO程序分析 上一篇文章 介绍了传统网络编程在处理高并发和大规模应用时通常面临性能瓶颈和资源管理问题&#xff0c;而 NIO 通过非阻塞 I/O 和选择器机制提供了更高效的解决方案。虽然 NIO 的 API 更复杂&#xff0c;但在高负载和高性能需…

先从路径优化开始学习FastPlanner之B样条曲线平滑路径(一):从拉格朗日插值到B样条曲线

参考B站视频学习 注&#xff1a;我会列出学习他人的博客&#xff0c;但我不涉及具体推导&#xff0c;原理讲解&#xff0c;旨在于理解必须概念后写代码出效果。 给若干点如何获得一条平滑的曲线&#xff1f; 两个方法插值、拟合 插值要经过给定点&#xff0c;拟合不用经过。 经…

Hostease的Windows虚拟主机如何设置错误页面

404错误设置主要用于定义当访问网站上不存在的页面时服务器应该如何响应。通常&#xff0c;404错误表示请求的页面或资源不存在。在Plesk面板中&#xff0c;你可以通过404错误设置来配置服务器对这种情况的处理方式。下面我就介绍如何在Hostease的Windows虚拟主机中设置404错误…

探索数据结构:图(一)之邻接矩阵与邻接表

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 图的定义 **图&#xff08;Graph&#xff09;**是数学和计算机科学中…

Mybatis-plus 创建自定义 FreeMarker 模板详细教程

FreeMarker 自定义模板官方步骤 网址&#xff1a;https://baomidou.com/reference/new-code-generator-configuration/#%E6%A8%A1%E6%9D%BF%E9%85%8D%E7%BD%AE-templateconfig &#xff08;页面往最下面拉为自定义模板相关内容&#xff09; 创建自定义FreeMarker 模板及使用…

案例分享—优秀ui设计作品赏析

多浏览国外优秀UI设计作品&#xff0c;深入分析其设计元素、色彩搭配、布局结构和交互方式&#xff0c;以理解其背后的设计理念和趋势。 在理解的基础上&#xff0c;尝试将国外设计风格中的精髓融入自己的设计中&#xff0c;同时结合国内用户的审美和使用习惯&#xff0c;进行创…

趣味算法------试用 6 和 9 组成的最大数字

目录 ​编辑 题目描述 解题思路 具体代码 总结 题目描述 给你一个仅由数字 6 和 9 组成的正整数 num。 你最多只能翻转一位数字&#xff0c;将 6 变成 9&#xff0c;或者把 9 变成 6 。 请返回你可以得到的最大数字。 输入格式 一个整数 输出格式 一个整数 输入输出…

【机器学习】决策树------迅速了其基本思想,Sklearn的决策树API及构建决策树的步骤!!!

目录 &#x1f354; 案例剖析 &#x1f354; 通过sklearn实现决策树分类并进一步认识决策树 &#x1f354; 基于规则构建决策树 &#x1f354; 构建决策树的三个步骤 &#x1f354; 小结 学习目标 &#x1f340; 了解决策树算法的基本思想 &#x1f340; 了解Sklearn的决策…

Linux WPA/WPA2/WPA3/IEEE 802.1X Supplicant

参考&#xff1a;wpa_supplicant 终端连接 wifi — Linux latest 文档 (gnu-linux.readthedocs.io) 为了管理无线网卡驱动&#xff0c;并且能正常连接到无线网络&#xff0c;你需要一个无线连接管理工具。 如何选择一个最佳的管理方法&#xff0c;将依赖于下面几个因素&#xf…

【数据结构】—— 树和二叉树

1、树的概念2、树的相关术语3、树的常见表示方法4、树的实际应用5、二叉树的相关概念和性质6、二叉树的顺序存储&#xff08;堆&#xff09;6.1 堆的概念6.2 堆的结构和接口6.3 堆的初始化和销毁6.4 堆的插入6.5 堆的删除6.5 取堆顶数据6.6 获取有效节点个数6.7 判空6.8 源代码…

免费SSL证书申请流程开启HTTPS,以及3个月到期解决方法

阿里云免费SSL证书申请流程2024年最新申请教程&#xff0c;阿里云免费SSL证书品牌是Digicert&#xff0c;免费单域名证书&#xff0c;一个阿里云账号可以免费申请20张SSL免费证书&#xff0c;免费时长为3个月&#xff08;之前是一年免费时长&#xff09;&#xff0c;免费SSL证书…