通过逐步探索的认识完成一个自己设计的对称加密算法(逆向分析 :D
例子与分析
为了初步了解 python 下的加密方式,我们先以以下密码分组链接模式(CBC)与一重 AES 算法结合,分析以下代码:
1 | # python |
关键部分代码分析
我们截取一个关键部分代码来进行理解(加密部分代码
生成随机的初始向量 IV
1
iv = os.urandom(16) # AES块大小为128位, 即16字节
- 作用: 初始向量(IV)用于AES的CBC模式,可以为相同的明文生成不同的密文。
-os.urandom(16)
: 这个函数生成一个16字节的随机数,用作IV。AES的块大小是128位,即16字节。创建加密对象
1
2cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()-
Cipher
: 创建一个Cipher对象,这个对象参数包括加密算法、模式和后端。
-algorithms.AES(key)
: 此处指明使用AES算法,并传入密钥key
,其中key
为按照 AES-256 要求随机生成的32位随机数。
-modes.CBC(iv)
: 指定加密模式为CBC(Cipher Block Chaining),并传入向量IV。
-backend=default_backend()
: 指定使用默认的加密后端。
-cipher.encryptor()
: 从Cipher对象中创建一个加密器对象(我将它理解成一个承载 cipher 的对象,因为后续的加密都要基于这个对象格式),用于后续的数据加密。填充数据以符合块大小要求
1
2padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()- PKCS7填充: AES算法要求数据长度必须是块大小的整数倍。PKCS7是一种填充标准,用于在数据的末尾添加足够的填充字节。
-padding.PKCS7(algorithms.AES.block_size)
: 创建一个PKCS7填充器,设置块大小为AES的块大小(128位)。
- 填充过程:padder.update(data)
处理原始数据,添加部分填充;padder.finalize()
完成填充过程,确保所有数据被正确填充。加密数据
1
encrypted = encryptor.update(padded_data) + encryptor.finalize()
-
encryptor.update(padded_data)
: 加密填充后的数据,包含以下流程:输入数据
update方法接受一部分(或全部)待加密的数据。在使用像CBC这样的块加密模式时,输入数据通常需要先进行填充,因为块加密算法要求输入数据的长度必须是块大小的整数倍(AES的块大小为128位或16字节)。数据分块
加密器在内部将输入的padded_data分成若干个块,每个块的大小等于加密算法的块大小。对于AES来说,即是每16字节划分为一个块。加密每个块
对于每一个数据块,加密器会按照指定的加密算法和模式进行处理。以上面的AES和CBC模式为例:
第一个数据块:使用初始向量(IV)与第一个数据块进行异或(XOR)操作,然后使用AES算法对结果进行加密。
后续数据块:每个后续的数据块都会与前一个加密后的块进行XOR操作,然后再进行AES加密。 即是说 python 将加密算法已经进行了封装,我们后续需要再行将此函数落实为代码执行。
生成密文
加密后的每个块会立即生成相应的密文块。encryptor.update(padded_data)方法将这些密文块合并,形成一个连续的密文数据流。返回密文
方法最终返回的是处理过的流密文。
-
encryptor.finalize()
: 完成加密过程,确保所有数据块都被加密。返回IV和加密的数据
1
return iv + encrypted
- 最后,函数返回的是IV和加密后的数据的组合。在解密时,需要使用相同的IV和密钥才能正确解密数据。通过将IV附加到加密数据的前面,可以方便地在解密时提取和使用。
详细函数解析
密码分组链接模式
1 | # python |
类定义和继承
1 | class CBC(ModeWithInitializationVector): |
这行代码定义了一个名为CBC的类,它继承自ModeWithInitializationVector
,CBC类是一个加密模式,它需要一个初始化向量(IV)。
类属性
1 | name = "CBC" |
一个类级别的属性,用于标识该加密模式的名称为”CBC”。
构造器(init 方法)
1 | def __init__(self, initialization_vector: bytes): |
这个方法是类的构造器,它接受一个参数initialization_vector,用作CBC模式的初始向量。
utils._check_byteslike("initialization_vector", initialization_vector)
:这行代码调用一个工具函数check_byteslike
,检查initialization_vector
是否是字节类型。这是为了确保类型安全,防止因类型错误而引发运行时错误。self._initialization_vector = initialization_vector
:这行代码将传入的初始化向量保存在实例变量_initialization_vector
中,以便于后续使用。
属性装饰器(@property)
1 | @property |
装饰器@property将方法变成一个可读属性。此处定义的属性为initialization_vector,用于获取实例的私有变量_initialization_vector
的值。这提供了一个安全的访问方式,外部代码可以通过属性访问IV值,而无法直接修改它,保持了数据的封装和安全性。
方法
1 | validate_for_algorithm = _check_iv_and_key_length |
将一个名为_check_iv_and_key_length
的函数或方法赋值给类的validate_for_algorithm
属性。validate_for_algorithm
用于在某些操作前验证初始化向量和密钥长度的正确性。确保在加密操作开始前,初始化向量和密钥符合特定算法的要求。
AES 类
1 | # python |
类定义和继承
1 | class AES(BlockCipherAlgorithm): |
定义了一个名为AES
的类,它继承自BlockCipherAlgorithm
。BlockCipherAlgorithm
为块密码算法(如AES)提供一些基本功能或规范。
类属性
1 | name = "AES" |
name = "AES"
:一个类级别的属性,用于表明该类实现的是AES算法。block_size = 128
:表示AES算法使用的块大小是128位,是AES算法的标准块大小。key_sizes = frozenset([128, 192, 256, 512])
:定义了有效的密钥大小(以位为单位)。AES标准支持128位、192位和256位密钥。额外的512位密钥大小说明可能是为了支持特定的AES变体或模式,如AES-256-XTS。
构造器(init 方法)
1 | def __init__(self, key: bytes): |
这个方法是类的构造器,它接受一个参数key,这个参数是一个字节序列,用作AES算法的密钥。
_verify_key_size(self, key)
:这行代码调用了一个函数_verify_key_size,传入当前实例和密钥。用于验证密钥的大小是否符合在key_sizes
属性中定义的有效大小。
属性装饰器(@property)
1 | @property |
装饰器@property将方法变成一个可读属性。定义的属性为key_size
,用于获取实例的密钥的大小(以位为单位)。len(self.key)
返回密钥的字节长度,乘以8转换为位长度。这提供了一个方便的方式来查询使用中的密钥大小。
发现通过对python
对于 AES 算法的实现进行解析较为困难,故直接调用,希望自己探究并在一定程度上实现 AES 的刘彻个。
详细 AES 执行流程参考 官网 AES 报告
完成与实现(.WAV .PNG .MP4)
现在采用 CBC + 多重(用户决定) AES 算法 来模拟标准的正常流程的函数:
1 | # python |
这里的 .mp3
格式由于 python 在 ffmpeg
库存在比较重大缺漏,解决起来较为复杂,且其它可以直接处理 .mp3
格式的库较少,所以没有给出具体方案,如果解决期待您的回复!
AES 算法实现与解析
以下提供一个简化的AES算法实现流程,以供初步分析
密钥扩展
1 | # python |
轮密钥加
1 | # python |
[s ^ k for s, k in zip(state, round_key)]
:state 和 round_key 是包含16个字节的列表。zip(state, round_key) 将这两个列表的相应元素配对,形成16个元组,每个元组包含来自两个列表的对应元素。列表推导式遍历这些元组,对每一对字节(即每个元组中的 s 和 k)执行按位异或操作,生成一个新的列表。
AES 加密与解密的区别:
主要涉及操作的逆向应用
加密过程:
· 字节替换(SubBytes):每个字节通过查S盒实现非线性替换。
· 行移位(ShiftRows):行移位操作,第一行不动,每行向左移位增加。
· 列混合(MixColumns):对状态矩阵的每一列应用一个线性变换,增加扩散性(最后一轮除外)。
· 轮密钥加(AddRoundKey):每一轮的状态与轮密钥进行异或操作。
解密过程:
· 逆向字节替换(InvSubBytes):使用逆S盒替换每个字节。
· 逆向行移位(InvShiftRows):与加密中的行移位相反,第一行不动,每行向右移位增加。
· 逆向列混合(InvMixColumns):应用列混合的逆变换。
· 轮密钥加(AddRoundKey):同加密过程。
- 密钥调度
在AES中,加密和解密都使用相同的主密钥生成一系列轮密钥,但是应用的顺序不同:
· 加密:按顺序使用生成的轮密钥。
· 解密:轮密钥的使用顺序是逆向的,即最后一个轮密钥先用,之后倒序使用其他轮密钥。
- 性能考虑
解密过程使用的是加密步骤的逆操作,特别是在实现InvMixColumns时,解密过程可能会比加密稍微复杂或低效。
对称加密算法的实现
加密和解密流程
1. 密钥和IV的生成
- 密钥生成:通过
os.urandom(32)
生成32字节(256位)的随机密钥。对于每一层加密,都生成一个新的密钥。 - IV生成:通过
os.urandom(16)
生成16字节(128位)的随机初始向量(IV)。同样,对于每一层加密,都生成一个新的IV。
2. 加密过程
- 逐层加密:加密过程中,使用AES算法和CBC模式(Cipher Block Chaining)。数据首先进行填充(PKCS7 padding),以确保数据长度是块大小(128位)的倍数。然后,使用生成的密钥和IV进行加密。每一层加密都会更新数据。
- IV的存储:最终加密的数据包含所有的IV和加密后的数据。IV是解密所需的,因此在加密数据的前面存储所有的IV。
3. 解密过程
- IV的提取:解密前,先从加密数据中提取出所有的IV。
- 逐层解密:解密过程中,从最后一层开始逐层解密。使用AES算法和CBC模式,结合相应的密钥和IV。解密后,数据进行去填充(PKCS7 unpadding)以还原原始数据。
文件操作
- WAV文件:通过
wave
模块读取和写入WAV文件的音频数据。read_wav_file
读取音频数据,write_wav_file
写入音频数据。 - PNG文件:通过
PIL
(Python Imaging Library)模块读取和写入PNG文件的图像数据。read_png_file
读取图像数据,write_png_file
写入图像数据。 - MP4文件:通过
ffmpeg
库读取和写入MP4文件的视频数据。read_video
读取视频数据,write_video
写入视频数据。