通过逐步探索的认识完成一个自己设计的对称加密算法(逆向分析 :D


例子与分析

为了初步了解 python 下的加密方式,我们先以以下密码分组链接模式(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
35
36
37
38
39
40
41
42
43
44
45
46
47
# python

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os

def encrypt(data, key):
   iv = os.urandom(16)  # AES 块大小为128位, 即16字节
   cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
   encryptor = cipher.encryptor()
   padder = padding.PKCS7(algorithms.AES.block_size).padder()
   padded_data = padder.update(data) + padder.finalize()
   encrypted = encryptor.update(padded_data) + encryptor.finalize()
   return iv + encrypted

def decrypt(encrypted, key):
   iv = encrypted[:16]
   encrypted_data = encrypted[16:]
   cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
   decryptor = cipher.decryptor()
   decrypted_padded = decryptor.update(encrypted_data) + decryptor.finalize()
   unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
   decrypted = unpadder.update(decrypted_padded) + unpadder.finalize()
   return decrypted

def main():
   key = os.urandom(32)  # AES-256要求密钥长度为32字节

   # 获取用户输入
   user_input = input("请输入您想要加密的内容:")
   data = user_input.encode('utf-8')  # 将输入的字符串转换为字节

   # 加密
   encrypted_data = encrypt(data, key)
   print("Encrypted:", encrypted_data)

   # 询问用户是否要解密
   if input("是否要解密加密后的数据?(y/n): ").lower() == 'y':
       # 解密
       decrypted_data = decrypt(encrypted_data, key)
       print("Decrypted:", decrypted_data.decode('utf-8'))  # 解码字节回字符串
   else:
       print("已选择不进行解密操作。")

if __name__ == "__main__":`
   main()

关键部分代码分析

我们截取一个关键部分代码来进行理解(加密部分代码

  1. 生成随机的初始向量 IV

    1
    iv = os.urandom(16)  # AES块大小为128位, 即16字节  

    - 作用: 初始向量(IV)用于AES的CBC模式,可以为相同的明文生成不同的密文。
    - os.urandom(16): 这个函数生成一个16字节的随机数,用作IV。AES的块大小是128位,即16字节。

  2. 创建加密对象

    1
    2
    cipher = 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 的对象,因为后续的加密都要基于这个对象格式),用于后续的数据加密。

  3. 填充数据以符合块大小要求

    1
    2
    padder = 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()完成填充过程,确保所有数据被正确填充。

  4. 加密数据

    1
    encrypted = encryptor.update(padded_data) + encryptor.finalize()  

    - encryptor.update(padded_data): 加密填充后的数据,包含以下流程:

    1. 输入数据
      update方法接受一部分(或全部)待加密的数据。在使用像CBC这样的块加密模式时,输入数据通常需要先进行填充,因为块加密算法要求输入数据的长度必须是块大小的整数倍(AES的块大小为128位或16字节)。

    2. 数据分块
      加密器在内部将输入的padded_data分成若干个块,每个块的大小等于加密算法的块大小。对于AES来说,即是每16字节划分为一个块。

    3. 加密每个块
      对于每一个数据块,加密器会按照指定的加密算法和模式进行处理。以上面的AES和CBC模式为例:

    第一个数据块:使用初始向量(IV)与第一个数据块进行异或(XOR)操作,然后使用AES算法对结果进行加密。
    后续数据块:每个后续的数据块都会与前一个加密后的块进行XOR操作,然后再进行AES加密。

    ​ 即是说 python 将加密算法已经进行了封装,我们后续需要再行将此函数落实为代码执行。

    1. 生成密文
      加密后的每个块会立即生成相应的密文块。encryptor.update(padded_data)方法将这些密文块合并,形成一个连续的密文数据流。

    2. 返回密文
      方法最终返回的是处理过的流密文。

    - encryptor.finalize(): 完成加密过程,确保所有数据块都被加密。

  5. 返回IV和加密的数据

    1
    return iv + encrypted  

    - 最后,函数返回的是IV和加密后的数据的组合。在解密时,需要使用相同的IV和密钥才能正确解密数据。通过将IV附加到加密数据的前面,可以方便地在解密时提取和使用。

详细函数解析

密码分组链接模式

1
2
3
4
5
6
7
8
9
10
11
# python

class CBC(ModeWithInitializationVector):
name = "CBC"
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector
validate_for_algorithm = _check_iv_and_key_length

类定义和继承

1
class CBC(ModeWithInitializationVector):

这行代码定义了一个名为CBC的类,它继承自ModeWithInitializationVector,CBC类是一个加密模式,它需要一个初始化向量(IV)。

类属性

1
name = "CBC"

一个类级别的属性,用于标识该加密模式的名称为”CBC”。

构造器(init 方法)

1
2
3
def __init__(self, initialization_vector: bytes):
utils._check_byteslike("initialization_vector", initialization_vector)
self._initialization_vector = initialization_vector

这个方法是类的构造器,它接受一个参数initialization_vector,用作CBC模式的初始向量。

utils._check_byteslike("initialization_vector", initialization_vector):这行代码调用一个工具函数check_byteslike,检查initialization_vector是否是字节类型。这是为了确保类型安全,防止因类型错误而引发运行时错误。
self._initialization_vector = initialization_vector:这行代码将传入的初始化向量保存在实例变量_initialization_vector中,以便于后续使用。

属性装饰器(@property)

1
2
3
@property
def initialization_vector(self) -> bytes:
return self._initialization_vector

装饰器@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
2
3
4
5
6
7
8
9
10
11
12
# python

class AES(BlockCipherAlgorithm):
name = "AES"
block_size = 128
\# 512 added to support AES-256-XTS, which uses 512-bit keys
key_sizes = frozenset([128, 192, 256, 512])
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)
@property
def key_size(self) -> int:
return len(self.key) * 8

类定义和继承

1
class AES(BlockCipherAlgorithm):

定义了一个名为AES的类,它继承自BlockCipherAlgorithmBlockCipherAlgorithm为块密码算法(如AES)提供一些基本功能或规范。

类属性

1
2
3
name = "AES"
block_size = 128
key_sizes = frozenset([128, 192, 256, 512])

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
2
def __init__(self, key: bytes):
self.key = _verify_key_size(self, key)

这个方法是类的构造器,它接受一个参数key,这个参数是一个字节序列,用作AES算法的密钥。

_verify_key_size(self, key):这行代码调用了一个函数_verify_key_size,传入当前实例和密钥。用于验证密钥的大小是否符合在key_sizes属性中定义的有效大小。

属性装饰器(@property)

1
2
3
@property
def key_size(self) -> int:
return len(self.key) * 8

装饰器@property将方法变成一个可读属性。定义的属性为key_size,用于获取实例的密钥的大小(以位为单位)。len(self.key) 返回密钥的字节长度,乘以8转换为位长度。这提供了一个方便的方式来查询使用中的密钥大小。

发现通过对python对于 AES 算法的实现进行解析较为困难,故直接调用,希望自己探究并在一定程度上实现 AES 的刘彻个。

详细 AES 执行流程参考 官网 AES 报告

完成与实现(.WAV .PNG .MP4)

现在采用 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# python

import wave
from PIL import Image
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os
import ffmpeg
# import tkinter as tk
# from tkinter import filedialog, messagebox, scrolledtext

# # -----------------------------------前置过程-----------------------------------------
# def preEncrypt():
# buff = int(buff_entry.get())
# file_type = file_type_var.get()
# input_path = input_entry.get()
# output_encrypted_path = output_encrypted_entry.get()
# output_decrypted_path = output_decrypted_entry.get()

# if file_type == 'WAV':
# keys = [os.urandom(32) for _ in range(buff)]
# original_data = read_wav_file(input_path)
# wav_params = wave.open(input_path).getparams()
# encrypted_data = encrypt(original_data, keys, buff)
# write_wav_file(output_encrypted_path, encrypted_data, wav_params)
# decrypted_data = decrypt(encrypted_data, keys, buff)
# write_wav_file(output_decrypted_path, decrypted_data, wav_params)
# elif file_type == 'PNG':
# keys = [os.urandom(32) for _ in range(buff)]
# original_data = read_png_file(input_path)
# img = Image.open(input_path)
# mode, size = img.mode, img.size
# encrypted_data = encrypt(original_data, keys, buff)
# write_png_file(output_encrypted_path, encrypted_data, mode, size)
# decrypted_data = decrypt(encrypted_data, keys, buff)
# write_png_file(output_decrypted_path, decrypted_data, mode, size)

# # 更新文本框内容
# results_text.config(state=tk.NORMAL) # 允许编辑文本框
# results_text.delete('1.0', tk.END) # 清除现有内容
# results_text.insert(tk.END, f"Encrypted Data (Partial/Hex):\n{encrypted_data[:64].hex()}\n\n")
# results_text.insert(tk.END, f"Decrypted Data (Partial/Text):\n{decrypted_data[:64]}\n")
# results_text.config(state=tk.DISABLED) # 禁止编辑文本框
# messagebox.showinfo("完成", "加密和解密已完成")


# -----------------------------------加解密-------------------------------------------
def encrypt(data, keys, buff):
# 为每层加密生成一个随机IV
ivs = [os.urandom(16) for _ in range(buff)]
encrypted_data = data

# 逐层加密数据
for i in range(buff):
key = keys[i]
iv = ivs[i]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(encrypted_data) + padder.finalize()
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()

# 将所有IV和加密的数据结合,所有IV对于解密是必需的
ivs_bytes = b''.join(ivs)
return ivs_bytes + encrypted_data

def decrypt(encrypted, keys, buff):
# 根据 buff 的值计算 IV 部分的字节长度
iv_length = 16 * buff
# 提取 IVs
ivs = [encrypted[i * 16:(i + 1) * 16] for i in range(buff)]
# 提取加密后的数据,跳过 IV 部分
encrypted_data = encrypted[iv_length:]

# 从最后一层开始逐层解密
for i in reversed(range(buff)):
key = keys[i]
iv = ivs[i]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_padded = decryptor.update(encrypted_data) + decryptor.finalize()
# 解除填充
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
encrypted_data = unpadder.update(decrypted_padded) + unpadder.finalize()

# 返回最终解密后的数据
return encrypted_data
# -----------------------------------WAV----------------------------------------
def read_wav_file(filename):
with wave.open(filename, 'rb') as wav_file:
data = wav_file.readframes(wav_file.getnframes())
return data

def write_wav_file(filename, data, params):
with wave.open(filename, 'wb') as wav_file:
wav_file.setparams(params)
wav_file.writeframes(data)

def encrypt_decrypt_wav(buff):
print("加/解密 WAV 文件\n")
input_path = input("请输入原始 WAV 文件路径:")
output_encrypted_path = input("请输入加密后保存的 WAV 文件路径:")
output_decrypted_path = input("请输入解密后保存的 WAV 文件路径:")

keys = [os.urandom(32) for _r in range(buff)] # 生成三个密钥

# 读取原始WAV文件
original_data = read_wav_file(input_path)
wav_params = wave.open(input_path).getparams()

# 加密
encrypted_data = encrypt(original_data, keys, buff)
print("--------------Encrypted-------------\n", encrypted_data)

# 写入加密后的数据到一个新的WAV文件(可选)
write_wav_file(output_encrypted_path, encrypted_data, wav_params)

# 解密
decrypted_data = decrypt(encrypted_data, keys, buff)
print("--------------Decrypted--------------\n", decrypted_data)

# 写入解密后的数据到一个新的WAV文件
write_wav_file(output_decrypted_path, decrypted_data, wav_params)


# -----------------------------PNG---------------------------------------
def read_png_file(filename):
"""读取PNG文件并返回数据."""
with Image.open(filename) as img:
data = img.tobytes()
return data

def write_png_file(filename, data, mode, size):
"""使用给定数据创建一个新的PNG文件."""
img = Image.frombytes(mode, size, data)
img.save(filename)

def encrypt_decrypt_png(buff):
print("加/解密 PNG 文件\n")
input_path = input("请输入原始 PNG 文件路径:")
output_encrypted_path = input("请输入加密后保存的 PNG 文件路径:")
output_decrypted_path = input("请输入解密后保存的 PNG 文件路径:")

# 生成密钥
keys = [os.urandom(32) for _ in range(buff)]

# 读取原始PNG文件
original_data = read_png_file(input_path)
img = Image.open(input_path)
mode, size = img.mode, img.size

# 加密
encrypted_data = encrypt(original_data, keys, buff)
print("--------------Encrypted-------------\n", encrypted_data)

# 写入加密后的数据到一个新的PNG文件
write_png_file(output_encrypted_path, encrypted_data, mode, size)

# 解密
decrypted_data = decrypt(encrypted_data, keys, buff)
print("--------------Decrypted--------------\n", decrypted_data)

# 写入解密后的数据到一个新的PNG文件
write_png_file(output_decrypted_path, decrypted_data, mode, size)

# ----------------------------MP3------------------------------------
# def convert_mp3_to_wav(source_file, target_file):
# """
# 使用ffmpeg将MP3文件转换为WAV格式。

# :param source_file: MP3文件的路径
# :param target_file: 生成的WAV文件的路径
# """
# # 调用ffmpeg进行转换
# # 创建ffmpeg输入流
# audio_input = ffmpeg.input(source_file)
# # 创建ffmpeg输出流,指定输出格式为wav
# audio_output = ffmpeg.output(audio_input, target_file, format='wav')
# # 执行ffmpeg命令
# ffmpeg.run(audio_output)
# # def read_mp3_file(file_path):
# # # 使用pydub读取MP3文件
# # audio = AudioSegment.from_mp3(file_path)
# # return audio

# # def write_mp3_file(file_path, audio_data):
# # # 导出音频数据到MP3
# # audio_data.export(file_path, format="mp3")

def encrypt_decrypt_mp3(buff):
print("加/解密 MP3 文件\n: 功能暂未开发")
# input_path = input("请输入原始 MP3 文件路径:")
# output_encrypted_path = input("请输入加密后保存的 WAV 文件路径:")
# output_decrypted_path = input("请输入解密后保存的 WAV 文件路径:")

# keys = [os.urandom(32) for _r in range(3)] # 生成三个密钥

# # 读取原始MP3文件
# path_without_extension, _ = os.path.splitext(input_path)
# path_with_extension = path_without_extension + '.wav'
# original_data = convert_mp3_to_wav(input_path, path_with_extension)

# wav_params = wave.open(path_with_extension).getparams()

# # 加密
# encrypted_data = encrypt(original_data, keys)
# print("--------------Encrypted-------------\n", encrypted_data)

# # 写入加密后的数据到一个新的WAV文件(可选)
# write_wav_file(output_encrypted_path, encrypted_data, wav_params)

# # 解密
# decrypted_data = decrypt(encrypted_data, keys)
# print("--------------Decrypted--------------\n", decrypted_data)

# # 写入解密后的数据到一个新的WAV文件
# write_wav_file(output_decrypted_path, decrypted_data, wav_params)
# ----------------------------------------------------------------------------

# ----------------------------MP4------------------------------------
def read_video(video_path):
probe = ffmpeg.probe(video_path)
video_info = next(stream for stream in probe['streams'] if stream['codec_type'] == 'video')
width = int(video_info['width'])
height = int(video_info['height'])
out, _ = ffmpeg.input(video_path).output('pipe:', format='rawvideo', pix_fmt='rgb24').run(capture_stdout=True)
return out, (width, height)

def write_video(video_data, output_path, size):
ffmpeg.input('pipe:', format='rawvideo', pix_fmt='rgb24', s=f'{size[0]}x{size[1]}').output(output_path, pix_fmt='yuv420p').run(input=video_data)

def encrypt_decrypt_mp4(buff):
input_path = input("请输入原始视频文件路径:")
output_encrypted_path = input("请输入加密后保存的视频文件路径:")
output_decrypted_path = input("请输入解密后保存的视频文件路径:")

keys = [os.urandom(32) for _ in range(buff)]

original_data, size = read_video(input_path)

encrypted_data = encrypt(original_data, keys, buff)
print("Video data encrypted.")

decrypted_data = decrypt(encrypted_data, keys, buff)
print("Video data decrypted.")

write_video(encrypted_data, output_encrypted_path, size)
write_video(decrypted_data, output_decrypted_path, size)
# ----------------------------------------------------------------------------

def main():
print("这是一个 AES 多重加密算法,目前支持加密 WAV, PNG 格式的文件:")
while True:
# 请求用户输入
input_string = input("请输入您需要的AES加密层数(20以下的正整数):")

try:
# 尝试将输入转换为整数
buff = int(input_string)

# 检查是否是20以下的正整数
if 1 <= buff <= 20:
break
else:
print("输入错误:请输入一个20以下的正整数。")

except ValueError:
# 处理无法转换为整数的情况
print("输入错误:请输入一个有效的整数。")

print("你可以有三种选择:")
print("1. 加/解密 .wav 文件;")
print("2. 加/解密 .mp3 文件;功能还待完善,敬请期待!")
print("3. 加/解密 .png 文件。")
print("4. 加/解密 .mp4 文件。")
choice = input("请输入您的选择:")
while ((choice != '1') & (choice != '2') & (choice != '3') & (choice != '4')):
print("无效的输入,请按要求输入!")
choice = input("请输入您的选择:")
if choice == '1':
encrypt_decrypt_wav(buff)
elif choice == '2':
encrypt_decrypt_mp3(buff)
elif choice == '3':
encrypt_decrypt_png(buff)
elif choice == '4':
encrypt_decrypt_mp4(buff)

if __name__ == "__main__":
main()# python

import wave
from PIL import Image
# import ffmpeg
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os

# -----------------------------------加解密-------------------------------------------
def encrypt(data, keys, buff):
# 为每层加密生成一个随机IV
ivs = [os.urandom(16) for _ in range(buff)]
encrypted_data = data

# 逐层加密数据
for i in range(buff):
key = keys[i]
iv = ivs[i]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(encrypted_data) + padder.finalize()
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()

# 将所有IV和加密的数据结合,所有IV对于解密是必需的
ivs_bytes = b''.join(ivs)
return ivs_bytes + encrypted_data

def decrypt(encrypted, keys, buff):
# 根据 buff 的值计算 IV 部分的字节长度
iv_length = 16 * buff
# 提取 IVs
ivs = [encrypted[i * 16:(i + 1) * 16] for i in range(buff)]
# 提取加密后的数据,跳过 IV 部分
encrypted_data = encrypted[iv_length:]

# 从最后一层开始逐层解密
for i in reversed(range(buff)):
key = keys[i]
iv = ivs[i]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_padded = decryptor.update(encrypted_data) + decryptor.finalize()
# 解除填充
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
encrypted_data = unpadder.update(decrypted_padded) + unpadder.finalize()

# 返回最终解密后的数据
return encrypted_data
# -----------------------------------WAV----------------------------------------
def read_wav_file(filename):
with wave.open(filename, 'rb') as wav_file:
data = wav_file.readframes(wav_file.getnframes())
return data

def write_wav_file(filename, data, params):
with wave.open(filename, 'wb') as wav_file:
wav_file.setparams(params)
wav_file.writeframes(data)

def encrypt_decrypt_wav(buff):
print("加/解密 WAV 文件\n")
input_path = input("请输入原始 WAV 文件路径:")
output_encrypted_path = input("请输入加密后保存的 WAV 文件路径:")
output_decrypted_path = input("请输入解密后保存的 WAV 文件路径:")

keys = [os.urandom(32) for _r in range(buff)] # 生成三个密钥

# 读取原始WAV文件
original_data = read_wav_file(input_path)
wav_params = wave.open(input_path).getparams()

# 加密
encrypted_data = encrypt(original_data, keys, buff)
print("--------------Encrypted-------------\n", encrypted_data)

# 写入加密后的数据到一个新的WAV文件(可选)
write_wav_file(output_encrypted_path, encrypted_data, wav_params)

# 解密
decrypted_data = decrypt(encrypted_data, keys, buff)
print("--------------Decrypted--------------\n", decrypted_data)

# 写入解密后的数据到一个新的WAV文件
write_wav_file(output_decrypted_path, decrypted_data, wav_params)

# ----------------------------MP3------------------------------------
# def convert_mp3_to_wav(source_file, target_file):
# """
# 使用ffmpeg将MP3文件转换为WAV格式。

# :param source_file: MP3文件的路径
# :param target_file: 生成的WAV文件的路径
# """
# # 调用ffmpeg进行转换
# # 创建ffmpeg输入流
# audio_input = ffmpeg.input(source_file)
# # 创建ffmpeg输出流,指定输出格式为wav
# audio_output = ffmpeg.output(audio_input, target_file, format='wav')
# # 执行ffmpeg命令
# ffmpeg.run(audio_output)
# # def read_mp3_file(file_path):
# # # 使用pydub读取MP3文件
# # audio = AudioSegment.from_mp3(file_path)
# # return audio

# # def write_mp3_file(file_path, audio_data):
# # # 导出音频数据到MP3
# # audio_data.export(file_path, format="mp3")

def encrypt_decrypt_mp3(buff):
print("加/解密 MP3 文件\n: 功能暂未开发")
# input_path = input("请输入原始 MP3 文件路径:")
# output_encrypted_path = input("请输入加密后保存的 WAV 文件路径:")
# output_decrypted_path = input("请输入解密后保存的 WAV 文件路径:")

# keys = [os.urandom(32) for _r in range(3)] # 生成三个密钥

# # 读取原始MP3文件
# path_without_extension, _ = os.path.splitext(input_path)
# path_with_extension = path_without_extension + '.wav'
# original_data = convert_mp3_to_wav(input_path, path_with_extension)

# wav_params = wave.open(path_with_extension).getparams()

# # 加密
# encrypted_data = encrypt(original_data, keys)
# print("--------------Encrypted-------------\n", encrypted_data)

# # 写入加密后的数据到一个新的WAV文件(可选)
# write_wav_file(output_encrypted_path, encrypted_data, wav_params)

# # 解密
# decrypted_data = decrypt(encrypted_data, keys)
# print("--------------Decrypted--------------\n", decrypted_data)

# # 写入解密后的数据到一个新的WAV文件
# write_wav_file(output_decrypted_path, decrypted_data, wav_params)

# -----------------------------PNG---------------------------------------
def read_png_file(filename):
"""读取PNG文件并返回数据."""
with Image.open(filename) as img:
data = img.tobytes()
return data

def write_png_file(filename, data, mode, size):
"""使用给定数据创建一个新的PNG文件."""
img = Image.frombytes(mode, size, data)
img.save(filename)

def encrypt_decrypt_png(buff):
print("加/解密 PNG 文件\n")
input_path = input("请输入原始 PNG 文件路径:")
output_encrypted_path = input("请输入加密后保存的 PNG 文件路径:")
output_decrypted_path = input("请输入解密后保存的 PNG 文件路径:")

# 生成密钥
keys = [os.urandom(32) for _ in range(buff)]

# 读取原始PNG文件
original_data = read_png_file(input_path)
img = Image.open(input_path)
mode, size = img.mode, img.size

# 加密
encrypted_data = encrypt(original_data, keys, buff)
print("--------------Encrypted-------------\n", encrypted_data)

# 写入加密后的数据到一个新的PNG文件
write_png_file(output_encrypted_path, encrypted_data, mode, size)

# 解密
decrypted_data = decrypt(encrypted_data, keys, buff)
print("--------------Decrypted--------------\n", decrypted_data)

# 写入解密后的数据到一个新的PNG文件
write_png_file(output_decrypted_path, decrypted_data, mode, size)

def main():
print("这是一个 AES 多重加密算法,目前支持加密 WAV, PNG 格式的文件:")
while True:
# 请求用户输入
input_string = input("请输入您需要的AES加密层数(20以下的正整数):")

try:
# 尝试将输入转换为整数
buff = int(input_string)

# 检查是否是20以下的正整数
if 1 <= buff <= 20:
break
else:
print("输入错误:请输入一个20以下的正整数。")

except ValueError:
# 处理无法转换为整数的情况
print("输入错误:请输入一个有效的整数。")

print("你可以有三种选择:")
print("1. 加/解密 .wav 文件;")
print("2. 加/解密 .mp3 文件;")
print("3. 加/解密 .png 文件。")
choice = input("请输入您的选择:")
while ((choice != '1') & (choice != '2') & (choice != '3')):
print("无效的输入,请按要求输入!")
choice = input("请输入您的选择:")
if choice == '1':
encrypt_decrypt_wav(buff)
elif choice == '2':
encrypt_decrypt_mp3(buff)
elif choice == '3':
encrypt_decrypt_png(buff)

if __name__ == "__main__":
main()

这里的 .mp3 格式由于 python 在 ffmpeg 库存在比较重大缺漏,解决起来较为复杂,且其它可以直接处理 .mp3 格式的库较少,所以没有给出具体方案,如果解决期待您的回复!

AES 算法实现与解析

以下提供一个简化的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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# python

def key_expansion(key):
# AES-128使用的完整S-box
s_box = [
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, 0xc1, 0x1d, 0xd5, 0x8e, 0x9e, 0x4e, 0x4b, 0xa1, 0x89, 0xd9, 0x8d, 0xfa, 0xbf, 0xea, 0xea,
0x11, 0xba, 0x4f, 0x24, 0xa1, 0x70, 0x6a, 0x00, 0x4e, 0x70, 0x3e, 0xd9, 0xfc, 0xdc, 0x4c, 0x20
]

# 轮常数Rcon
rcon = [
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
]

def key_expansion(key):
key_size = len(key)
key_symbols = key_size // 4
rounds = 10 if key_size == 16 else (12 if key_size == 24 else 14)
expanded_key = [0] * (4 * (rounds + 1) * 4)
expanded_key[:key_size] = list(key)

# 用于扩展的临时变量
temp = [0] * 4

# 开始扩展
i = key_symbols
while i < 4 * (rounds + 1):
for t in range(4):
temp[t] = expanded_key[(i - 1) * 4 + t]

if i % key_symbols == 0:
# 旋转
temp = temp[1:] + temp[:1]
# S-box substitution
temp = [Sbox[b] for b in temp]
# Rcon
temp[0] ^= Rcon[i // key_symbols - 1]

for t in range(4):
expanded_key[i * 4 + t] = expanded_key[(i - key_symbols) * 4 + t] ^ temp[t]
i += 1

return expanded_key

key = b'\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c' # 16字节密钥
expanded_key = key_expansion(key)
print(expanded_key)

轮密钥加

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
# python

def add_round_key(state, round_key):
"""将轮密钥添加到状态数组中"""
new_state = [[0] * 4 for _ in range(4)]
for r in range(4):
for c in range(4):
new_state[r][c] = state[r][c] ^ round_key[r][c]
return new_state

# 示例的状态数组和轮密钥(仅为示例,通常由密钥扩展生成)
state = [
[0x32, 0x88, 0x31, 0xe0],
[0x43, 0x5a, 0x31, 0x37],
[0xf6, 0x30, 0x98, 0x07],
[0xa8, 0x8d, 0xa2, 0x34]
]

round_key = [
[0x2b, 0x28, 0xab, 0x09],
[0x7e, 0xae, 0xf7, 0xcf],
[0x15, 0xd2, 0x15, 0x4f],
[0x16, 0xa6, 0x88, 0x3c]
]

# 应用轮密钥加
new_state = add_round_key(state, round_key)
print("新的状态数组:")
for row in new_state:
print(" ".join(f"{x:02x}" for x in row))

[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):同加密过程。

  1. 密钥调度
    在AES中,加密和解密都使用相同的主密钥生成一系列轮密钥,但是应用的顺序不同:

​ · 加密:按顺序使用生成的轮密钥。
​ · 解密:轮密钥的使用顺序是逆向的,即最后一个轮密钥先用,之后倒序使用其他轮密钥。

  1. 性能考虑
    解密过程使用的是加密步骤的逆操作,特别是在实现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写入视频数据。