4012 字
20 分钟
AES加密算法

众所周知,AES 算法是我们在安卓逆向中的一个比较重要的算法,掌握它的算法细节对于我们逆向无疑是有很大的帮助的
这一块呢,看的是龙哥的文章来学习的,为了记个笔记,就照搬了一下龙哥的图,致敬大佬

AES 分类#

分类密钥长度轮密钥长度扩展密钥长度分组长度加密轮数
AES-12816 字节16 字节16*11=17216 字节10
AES-19224 字节16 字节16*13=20816 字节12
AES-25632 字节16 字节16*15=24016 字节14

上面的这三个算法,除了密钥编排算法不同和加密的轮数不一样之外,其余的计算逻辑都是相同的
CBC 模式 IV 都是 16 个字节,CBC 计算逻辑相同

  • AES-128 密钥编排算法中 K0 是原始密钥,共 16 个字节
  • AES-192 密钥编排算法中 K0 和 K1 的前半部分是原始密钥,共 24 个字节
  • AES-256 密钥编排算法中 K0 和 K1 是原始密钥,共 32 字节

填充模式#

在 AES 中,识别常见的明文填充模式也是比较重要的,下面是几种常见的填充模式:

PKCS #7 Padding#

这个是目前最标准,最通用的填充方式

  • 规则:假设需要填充 N 个字节,则填充的 N 个字节的值都是 N(16 进制)
  • 特殊情况:如果明文长度正好是 16 字节的整数倍,则额外填充一个完整的块(即 16 个字节,每个字节都是 0x10,即十进制的 16),以便解密时能正确的移除填充
  • 示例:
    • 缺 3 个字节: 03 03 03
    • 缺 1 个字节: 01
    • 正好整除: 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10

PKCS #5 Padding#

PKCS #5 标准严格规定分组长度 8 字节(64 位)

  • 计算缺失长度:计算明文距离下一个 8 字节倍数还差多少字节,记为 N
  • 填充内容:在明文末尾添加 N 个字节,每个字节的值都等于 N(16 进制表示)
  • 特殊情况:如果明文长度正好是 8 字节的整数倍,则额外填充一个完整的块(即填充 8 个字节,每个字节的值都是 0x08)
  • 示例:
    • 明文长度不足 8 个字节
      • 明文: A B C D E (5 字节)
      • 填充: A B C D E 03 03 03
    • 明文长度超过 8 个字节但未满 16 字节
      • 明文: A B C D E F G H I J (10 字节)
      • 填充: A B C D E F G H I J 06 06 06 06 06 06
    • 明文长度正好是 8 的倍数
      • 明文: A B C D E F G H (8 字节)
      • 填充: A B C D E F G H 08 08 08 08 08 08 08 08

Zero Padding(零填充)#

直接填充 0,缺 3 个字节则填充 00 00 00

No Padding#

  • 规则:不进行任何填充
  • 使用条件:
    • 明文长度严格等于 16 的倍数
    • 或者使用了不需要填充的工作模式

工作模式#

下面我总结成了一个表

模式是否需要填充是否需要 iv
ECB
CBC
CTR
GCM
CFB
OFB

计算流程#

下面主要以 AES-128 为例,其他的也是一样的
AES-128 接收 16 字节的明文输入,16 字节的密钥,输出 16 字节的密文结果
设置 key 为 2b7e151628aed2a6abf7158809cf4f3c,明文为 00112233445566778899aabbccddeeff
AES 的整体可以分成左右两块,即明文的处理和密钥的编排
明文的处理主体是一个初始化轮密钥加和十轮运算,在初始化轮密钥加十轮运算中都需要使用密钥编排的结果
密钥编排将 16 个字节经过运算推演出 11 组轮密钥,每一组 16 个字节,称之为 K0,K1…K10

密钥编排#

首先来看一下密钥是如何算出来扩展密钥的,上面设置的 key 是 2b7e151628aed2a6abf7158809cf4f3c,为了区分密钥和密钥编排后的轮密钥,我们将此时的密钥叫做主密钥
在 AES-128 中,密钥扩展后得到 17×16=17617 \times 16=176 字节,使用时逐 16 字节划分成 K0,K1,......,K10K_0,K_1,......,K_{10},但是在生成时,它是逐 4 字节生成的,即 44*4。不妨用数组来描述它,即一个包含了 44 个元素的数组,叫 WW
这 44 个元素生成的规则有三种,如下图所示:

不同颜色代表了不同规则,最上方蓝色区域的就是主密钥本身切成四段
左侧的红色部分,W4,W8,W12,......,W40W_4,W_8,W_{12},......,W_{40} 的生成稍复杂一点

Wn=g(Wn1)xorWn4W_n= g(W_{n-1}) xor W_{n-4}

xor 是异或运算,比如: W4=g(W3)xorW0W_4 = g(W_3)\quad xor\quad W_0 。g(当前元素前面那个元素)异或当前元素头顶上那个元素
那么关键点就是这个 g 函数了,g 函数一个分三个步骤—循环左移,S 盒替换,字节异或,下面以 W4W_4 运算中所需的 W3W_3 为例。

W3=09cf4f3cW_3=09cf4f3c\\

首先是循环左移,规则固定—将最左边的一个字节挪到右边即可

第二步就是 S 盒替换,将数值本身作为索引取出 S 数组中对应的值。S 盒是固定的,在逆向的过程中经常会用来判断 AES 的标志

SBox = [
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
]
num = 0x13
result = SBox[num]
print(hex(result))
### 7d

虽然 S 盒背后有十分复杂的知识,但是我们逆向算法,一般用不着去了解

最后一个步骤简单,将上一步得到的结果中的最高字节与一个固定常量异或。W4W_4 的生成是第一个,用如下 rcon 表的第一个元素 0x01。W40W_{40} 即第 10 次,用最后一个元素 0x36

rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]

最终结果

下面红色的部分同理
接下来就应该看一下橙色的部分了,和红色的部分类似,去掉 g 函数即可

例如: W5=W4xorW1=0xa0fafe17xor0x28aed2a6=0x88542cb1W_5 = W_4\quad xor\quad W_1=0xa0fafe17\quad xor\quad 0x28aed2a6 = 0x88542cb1
如下就是密钥编排的完整的算法:

Sbox = (
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,
)
Rcon = (0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36)
def text2matrix(text):
matrix = []
for i in range(16):
byte = (text >> (8 * (15 - i))) & 0xFF
if i % 4 == 0:
matrix.append([byte])
else:
matrix[i // 4].append(byte)
return matrix
def shiftRound(array, num):
'''
:param array: 需要循环左移的数组
:param num: 循环左移的位数
:return: 使用Python切片,返回循环左移num个单位的array
'''
return array[num:] + array[:num]
def g(array, index):
'''
g 函数
:param array: 待处理的四字节数组
:index:从1-10,每次使用Rcon中不同的数
'''
# 首先循环左移1位
array = shiftRound(array, 1)
# 字节替换
array = [Sbox[i] for i in array]
# 首字节和rcon中对应元素异或
array = [(Rcon[index] ^ array[0])] + array[1:]
return array
def xorTwoArray(array1, array2):
'''
返回两个数组逐元素异或的新数组
:param array1: 一个array
:param array2: 另一个array
:return:
'''
assert len(array1) == len(array2)
return [array1[i] ^ array2[i] for i in range(len(array1))]
def showRoundKeys(kList):
for i in range(len(kList)):
print("K%02d:" %i +"".join("%02x" % k for k in kList[i]))
def keyExpand(key):
master_key = text2matrix(key)
round_keys = [[0] * 4 for i in range(44)]
# 规则一(图中红色部分)
for i in range(4):
round_keys[i] = master_key[i]
for i in range(4, 4 * 11):
# 规则二(图中红色部分)
if i % 4 == 0:
round_keys[i] = xorTwoArray(g(round_keys[i - 1], i // 4), round_keys[i - 4])
# 规则三(图中橙色部分)
else:
round_keys[i] = xorTwoArray(round_keys[i - 1], round_keys[i - 4])
# 将轮密钥从44*4转成11*16,方便后面在明文的运算里使用
kList = [[] for i in range(11)]
for i in range(len(round_keys)):
kList[i//4] += round_keys[i]
showRoundKeys(kList)
return kList
input_bytes = 0x00112233445566778899aabbccddeeff
key = 0x2b7e151628aed2a6abf7158809cf4f3c
kList = keyExpand(key)

运行结果如下:

Terminal window
K00:2b7e151628aed2a6abf7158809cf4f3c
K01:a0fafe1788542cb123a339392a6c7605
K02:f2c295f27a96b9435935807a7359f67f
K03:3d80477d4716fe3e1e237e446d7a883b
K04:ef44a541a8525b7fb671253bdb0bad00
K05:d4d1c6f87c839d87caf2b8bc11f915bc
K06:6d88a37a110b3efddbf98641ca0093fd
K07:4e54f70e5f5fc9f384a64fb24ea6dc4f
K08:ead27321b58dbad2312bf5607f8d292f
K09:ac7766f319fadc2128d12941575c006e
K10:d014f9a8c9ee2589e13f0cc8b6630ca6

明文处理#

密钥编排部分说完了,接下来该看一下明文的处理了即上面流程图中左边的部分
首先,需要调整明文的格式,在 AES 中,数据以 statestate 的形式计算,中间存储与传输,中文名即状态
从明文转到 state 形式很简单,以我们上面的那个明文为例: 00112233445566778899aabbccddeeff,从上到下,从左至右

接着,轮密钥加,为什么叫轮密钥加呢?因为在算法中加就是异或
这个是第一次轮密钥加的步骤,所以也叫做初始轮密钥加,也就是将明文的 state 状态与初始轮密钥进行逐字节异或
接下来就是 10 轮主运算,看如下的伪代码,可以清楚的看到一轮运算中有什么,以及第十轮和前九轮有什么区别

初始的明文转 state 和最后的 state 转明文自不用说,然后是初始轮密钥,使用 K0K_0
前 9 轮运算中,包含四个步骤:字节替换,循环左移,列混淆,轮密钥加
第 10 轮种,包含三个步骤:字节替换,循环左移,轮密钥加,相比较于前 9 轮,少了一个列混淆,其余的都是相同的
而字节替换步骤,和密钥编排中的 S 盒替换完全一致
循环左移,和密钥编排中的循环左移类似,但不完全相同。在密钥编排中,函数中也需要循环左移,但其中待处理的数据仅有一行,而明文编排中有 4 行,其循环左移规则如下:
第一行不循环左移,第二行循环左移 1 字节,第三行循环左移 2 字节,第四行循环左移 3 字节

列混淆有点麻烦啊,就不写了吧
所有的代码如下:

Sbox = (
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,
)
Rcon = (0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36)
def text2matrix(text):
matrix = []
for i in range(16):
byte = (text >> (8 * (15 - i))) & 0xFF
if i % 4 == 0:
matrix.append([byte])
else:
matrix[i // 4].append(byte)
return matrix
def shiftRound(array, num):
'''
:param array: 需要循环左移的数组
:param num: 循环左移的位数
:return: 使用Python切片,返回循环左移num个单位的array
'''
return array[num:] + array[:num]
def g(array, index):
'''
g 函数
:param array: 待处理的四字节数组
:index:从1-10,每次使用Rcon中不同的数
'''
# 首先循环左移1位
array = shiftRound(array, 1)
# 字节替换
array = [Sbox[i] for i in array]
# 首字节和rcon中对应元素异或
array = [(Rcon[index] ^ array[0])] + array[1:]
return array
def xorTwoArray(array1, array2):
'''
返回两个数组逐元素异或的新数组
:param array1: 一个array
:param array2: 另一个array
:return:
'''
assert len(array1) == len(array2)
return [array1[i] ^ array2[i] for i in range(len(array1))]
def showRoundKeys(round_keys):
# 将轮密钥从44*4转成11*16
kList = [[] for i in range(11)]
for i in range(len(round_keys)):
kList[i // 4] += round_keys[i]
for i in range(len(kList)):
print("K%02d:" % i + "".join("%02x" % k for k in kList[i]))
def keyExpand(key):
master_key = text2matrix(key)
round_keys = [[0] * 4 for i in range(44)]
# 规则一(图中红色部分)
for i in range(4):
round_keys[i] = master_key[i]
for i in range(4, 4 * 11):
# 规则二(图中红色部分)
if i % 4 == 0:
round_keys[i] = xorTwoArray(g(round_keys[i - 1], i // 4), round_keys[i - 4])
# 规则三(图中橙色部分)
else:
round_keys[i] = xorTwoArray(round_keys[i - 1], round_keys[i - 4])
showRoundKeys(round_keys)
return round_keys
def AddRoundKeys(state, roundKey):
result = [[] for i in range(4)]
for i in range(4):
result[i] = xorTwoArray(state[i], roundKey[i])
return result
def SubBytes(state):
result = [[] for i in range(4)]
for i in range(4):
result[i] = [Sbox[i] for i in state[i]]
return result
def ShiftRows(s):
s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1]
s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]
s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3]
return s
def mul_by_02(num):
if num < 0x80:
res = (num << 1)
else:
res = (num << 1) ^ 0x1b
return res % 0x100
def mul_by_03(num):
return mul_by_02(num) ^ num
def MixColumns(state):
for i in range(4):
s0 = mul_by_02(state[i][0]) ^ mul_by_03(state[i][1]) ^ state[i][2] ^ state[i][3]
s1 = state[i][0] ^ mul_by_02(state[i][1]) ^ mul_by_03(state[i][2]) ^ state[i][3]
s2 = state[i][0] ^ state[i][1] ^ mul_by_02(state[i][2]) ^ mul_by_03(state[i][3])
s3 = mul_by_03(state[i][0]) ^ state[i][1] ^ state[i][2] ^ mul_by_02(state[i][3])
state[i][0] = s0
state[i][1] = s1
state[i][2] = s2
state[i][3] = s3
return state
def state2Text(state):
text = sum(state, [])
return "".join("%02x" % k for k in text)
def encrypt(input_bytes, kList):
'''
:param input_bytes: 输入的明文
:param kList: K0-K10
:return:
'''
plainState = text2matrix(input_bytes)
# 初始轮密钥加
state = AddRoundKeys(plainState, kList[0:4])
for i in range(1, 10):
state = SubBytes(state)
state = ShiftRows(state)
if i ==9:
state[0][0] = 0
state = MixColumns(state)
state = AddRoundKeys(state, kList[4 * i:4 * (i + 1)])
state = SubBytes(state)
state = ShiftRows(state)
state = AddRoundKeys(state, kList[40:44])
return state
input_bytes = 0x00112233445566778899aabbccddeeff
key = 0x2b7e151628aed2a6abf7158809cf4f3c
kList = keyExpand(key)
cipherState = encrypt(input_bytes, kList)
cipher = state2Text(cipherState)
print(cipher)

AES CBC 模式的计算流程#

当 AES 加密是 CBC 模式的时候,明文首先会与 IV 异或,然后将结果进行块加密,输出明文,同时本次输出密文作为下一个块加密的 IV
IV 同样也需要转为 state 然后与明文的 state 进行异或
例如 IV 是 00112233445566778899aabbccddeeff

仅需要修改以下代码就可以实现 AES 的 CBC 加密

def InputXorIv(state, ivState):
result = [[] for i in range(4)]
for i in range(4):
result[i] = xorTwoArray(state[i], ivState[i])
return result
def encrypt(input_bytes, iv, kList):
'''
:param input_bytes: 输入的明文
:param kList: K0-K10
:return:
'''
ivState = text2matrix(iv)
plainState = text2matrix(input_bytes)
# 明文与iv异或
plainState = InputXorIv(plainState, ivState)
# 初始轮密钥加 K0
state = AddRoundKeys(plainState, kList[0:4])
for i in range(1, 10):
# 字节替换
state = SubBytes(state)
# 循环左移
state = ShiftRows(state)
# 列混淆
state = MixColumns(state)
# 轮密钥加 K1-K9
state = AddRoundKeys(state, kList[4 * i:4 * (i + 1)])
# 字节替换
state = SubBytes(state)
# 循环左移
state = ShiftRows(state)
# 轮密钥加 K10
state = AddRoundKeys(state, kList[40:44])
return state
input_bytes = 0x00112233445566778899aabbccddeeff
key = 0x2b7e151628aed2a6abf7158809cf4f3c
iv = 0x2b7e151628aed2a6abf7158809cf4f3c
kList = keyExpand(key)
cipherState = encrypt(input_bytes, iv, kList)
cipher = state2Text(cipherState)
print(cipher)

参考文章#

龙哥星球文章
https://www.yuque.com/nanren-w8l2z/xgu63m/uoff9ovsmhqki9wh?singleDoc#cjBWY

AES加密算法
https://819229.xyz/posts/aes算法/
作者
0xnonce
发布于
2026-02-24
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

评论

正在加载评论...
0/2000

    最后更新于 ,距今已过 187

    部分内容可能已过时