基本知识介绍
AES算法
AES加密流程
密钥扩展: 将初始密钥扩展为多个子密钥,进行后续加密处理。
初始轮:
- 轮密钥加: 将数据块与第一个子密钥通过异或运算进行结合。
主要轮次 : 根据密钥长度,AES 有 10、12 或 14 个主要轮次。每个主要轮次包括四个步骤:
- 字节代换: 使用一个固定的S-盒对每个字节进行替换。
- 行移位 : 对状态矩阵的每一行进行循环左移操作。
- 列混淆 : 对状态矩阵的每一列进行线性变换。
- 轮密钥加: 将当前状态与本轮的子密钥进行异或运算。
最终轮: 与主要轮次类似,但省略了“列混淆”步骤。
字节代换-------> 行移位-------->轮密钥加
CFB模式
CFB(Cipher Feedback)是块密码的一种工作模式,它将块密码转换为流密码,用于加密和解密数据。
CFB工作模式的主要步骤
初始化向量 (IV): 选择一个随机的初始化向量,这个向量的长度等于块大小。
块加密:使用AES对IV进行加密,生成一个密钥流块。
生成密文:将密钥流块与明文块进行异或运算,生成密文块。
更新IV:将密文块的一部分作为新的IV,用于下一个明文块的加密。
设计步骤
AES加解密实现
定义AES加密和解密函数,使用Crypto.Cipher
模块中的AES算法
# 加密函数
def aes_encrypt(data, key, iv):
cipher = AES.new(key, AES.MODE_CFB, iv)
return cipher.encrypt(data)
# 解密函数
def aes_decrypt(encrypted_data, key, iv):
cipher = AES.new(key, AES.MODE_CFB, iv)
return cipher.decrypt(encrypted_data)
aes_encrypt
函数接收明文数据data
、密钥key
和初始化向量iv
,使用AES的CFB模式进行加密,并返回加密后的数据。
aes_decrypt
函数接收密文数据encrypted_data
、密钥key
和初始化向量iv
,同样使用AES的CFB模式进行解密,并返回解密后的数据。
将完整视频转为帧
使用cv2.VideoCapture
来读取视频文件,并将其转换为帧
cap = cv2.VideoCapture(video_file_path)
if not cap.isOpened():
print(f"Error opening video file: {video_file_path}")
return
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
fps = int(cap.get(cv2.CAP_PROP_FPS))
cap.get(3)
和cap.get(4)
分别获取视频的宽度和高度,cap.get(cv2.CAP_PROP_FPS)来
获取视频的帧率。
对视频帧加密
定义encrypt_video_frame
函数对单个视频帧进行加密
def encrypt_video_frame(frame, key, iv):
frame_bytes = frame.tobytes()
encrypted_frame_bytes = aes_encrypt(frame_bytes, key, iv)
return np.frombuffer(encrypted_frame_bytes, dtype=np.uint8).reshape(frame.shape)
frame.tobytes()
将视频帧转换为字节序列,然后用aes_encrypt
函数对字节序列进行加密。
np.frombuffer
将加密后的字节序列转换回NumPy数组,使用reshape
调整为原始帧的形状。
对视频帧解密
定义decrypt_video_frame
函数对单个视频帧进行解密
def decrypt_video_frame(encrypted_frame_bytes, key, iv, frame_shape):
try:
decrypted_frame_bytes = aes_decrypt(encrypted_frame_bytes, key, iv)
decrypted_frame = np.frombuffer(decrypted_frame_bytes, dtype=np.uint8)
return decrypted_frame.reshape(frame_shape)
except Exception as e:
print(f"解密失败:{e}")
return None
aes_decrypt
函数对加密的视频帧进行解密,np.frombuffer
将解密后的字节序列转换回NumPy数组,然后使用reshape
调整为原始帧的形状,如果解密失败则返回None。
核心主程序实现
打开视频文件并检查是否成功
cap = cv2.VideoCapture(video_file_path)
if not cap.isOpened():
print(f"Error opening video file: {video_file_path}")
return
首先打开传入的视频文件路径video_file_path
,并检查是否成功打开。如果打开失败则打印错误信息并退出函数。
获取视频信息
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
fps = int(cap.get(cv2.CAP_PROP_FPS))
分别获取视频的宽度、高度和帧率
创建视频写入对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
output_file_path = 'encrypted_video.mp4' if encrypt else 'decrypted_video.mp4'
out = cv2.VideoWriter(output_file_path, fourcc, fps, (frame_width, frame_height))
根据是否加密,设置输出文件的路径(名)
创建cv2.VideoWriter
对象将处理后的帧写入新的视频文件中
初始化变量
frame_count = 0
encrypted_frame_indices = []
encrypted_frames_data = []
进行初始化变量帧计数器frame_count
,加密帧索引列表encrypted_frame_indices
和加密帧数据列表encrypted_frames_data
。
加密流程
if encrypt:
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
if (frame_count - 1) % 400 < 150:
encrypted_frame = encrypt_video_frame(frame, key, iv)
encrypted_frames_data.append(encrypted_frame)
encrypted_frame_indices.append((frame_count - 1, len(encrypted_frames_data) - 1))
out.write(encrypted_frame)
else:
out.write(frame)
with open('encrypted_frame_indices.txt', 'w') as f:
for original_index, encrypted_index in encrypted_frame_indices:
f.write(f"{original_index} --> {encrypted_index}\n")
np.save('encrypted_frames_data.npy', encrypted_frames_data)
详细加密流程,逐帧读取视频并根据条件(frame_count - 1) % 400 < 150(每400帧加密一次,每次加密150帧)来
决定是否加密。加密的帧会被添加到encrypted_frames_data
中,并记录索引映射到encrypted_frame_indices
。最后,将加密帧索引映射保存到文件encrypted_frame_indices.txt
中,将加密帧数据保存到文件encrypted_frames_data.npy
中。
解密流程
else:
with open('encrypted_frame_indices.txt', 'r') as f:
encrypted_frame_indices = [(int(line.split(' --> ')[0]), int(line.split(' --> ')[1])) for line in f]
encrypted_frames_data = np.load('encrypted_frames_data.npy', allow_pickle=True)
encrypted_frame_indices_dict = {orig_index: enc_index for orig_index, enc_index in encrypted_frame_indices}
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
if frame_count - 1 in encrypted_frame_indices_dict:
enc_index = encrypted_frame_indices_dict[frame_count - 1]
encrypted_frame = encrypted_frames_data[enc_index]
decrypted_frame = decrypt_video_frame(encrypted_frame.tobytes(), key, iv, frame.shape)
if decrypted_frame is not None:
out.write(decrypted_frame)
else:
print("某帧解密失败,使用原始帧替代")
out.write(frame)
else:
out.write(frame)
详细解密流程,首先从文件encrypted_frame_indices.txt
中读取加密帧的索引映射,并从文件encrypted_frames_data.npy
中加载加密后的帧数据。接着逐帧读取视频,对于加密的帧,根据索引映射找到对应的加密帧数据,并进行解密。如果解密成功,就将解密后的帧写入视频文件;如果解密失败,打印错误信息并使用原始帧替代。
释放资源打印完成信息
cap.release()
out.release()
cv2.destroyAllWindows()
print(f"\n{output_file_path} 已生成。")
释放视频捕获和视频写入对象,关闭所有OpenCV窗口;打印完成信息,输出文件的路径。
实现结果
4k.mp4 原视频
encrypted_video.mp4 加密视频
encrypted_frames_data.npy 加密的帧数据
encrypted_frame_indices.txt 原视频帧与加密帧对应关系
decrypted_video.mp4 解密视频
加密后的视频内容