目录
0. 何为序列密码?
1. OTP加解密原理
2. 完整加解密过程
2-1 密钥生成函数
2-2 加密函数
2-3 解密函数
2-4 综合一下
3. 举一反三
3-1 对图片的加密
3-2 对图片的解密
3-3 对视频的加密
3-4 对视频的解密
4. 最后
序列密码(Stream Cipher),也称为流密码,是一种对称密钥加密算法。它将明文消息按字符或比特逐位进行加密,而不是像分组密码那样将明文分成固定长度的块进行加密。
- 特点
- 加密速度快:由于是逐位加密,不需要对明文进行分组和填充等操作,因此加密和解密速度通常较快,适用于对实时性要求较高的应用,如视频流、音频流的加密。
- 错误传播小:在加密过程中,如果某个比特发生错误,只会影响到该比特对应的密文,不会像分组密码那样导致整个分组的错误传播,对后续的解密影响相对较小。
- 需要同步:发送方和接收方必须保持严格的同步,以确保生成相同的密钥流。如果同步出现问题,例如数据丢失或插入,可能会导致解密失败。
- 密钥流的安全性至关重要:因为序列密码的安全性主要依赖于密钥流的随机性和不可预测性。如果密钥流被攻击者预测或破解,那么整个加密系统就会被攻破。
一次性密码本(OTP)是一种理论上具有完美保密性的加密方案。其核心原理基于以下几点:
1. 密钥:使用一个与明文长度相同且 完全随机 的密钥,这个密钥 只能使用一次 ,使用后就丢弃。 (C是密文,P是明文,K是密钥)
2. 加密过程:将明文与密钥进行逐位异或(XOR)操作,得到密文。即:C=P^K
3. 解密过程:将密文与相同的密钥再次进行逐位异或操作,就可以还原出明文。 因为异或运算具有自反性,即 P=C^K=(P^K)^K
import os
def generate_key(length):
return os.urandom(length)
解释:
使用 os.urandom(length) 生成length 长度的随机密钥。
为什么不用 random 呢?因为random生成的是伪随机数,并非像 os.urandom 那样具备密码学安全的随机数。
def encrypt(plaintext): # 接收明文参数
# 获取加密密钥
key = generate_key(len(plaintext))
# 异或加密,得到密文
ciphertext = bytes([a ^ b for a, b in zip(plaintext, key)])
return ciphertext, key
解释:
zip() 函数会从每个可迭代对象中依次取出一个元素,组成一个元组,然后将这些元组作为迭代器的元素返回。
(当最短的可迭代对象耗尽时,迭代过程就会停止,即最终返回的迭代器的长度等于最短的可迭代对象的长度。)
例如:
list1 = [1, 2, 3] list2 = ['a', 'b', 'c'] zipped = zip(list1, list2) # 结果: [(1, 'a'), (2, 'b'), (3, 'c')]
注意使用 bytes() 转换为字节类型。
def decrypt(ciphertext, key): # 接收两个参数
# 异或运算的可逆性
plaintext = bytes([a ^ b for a, b in zip(ciphertext, key)])
return plaintext
import os
def generate_key(length):
return os.urandom(length)
def encrypt(plaintext):
# 获取加密密钥
key = generate_key(len(plaintext))
# 异或加密
ciphertext = bytes([a ^ b for a, b in zip(plaintext, key)])
return ciphertext, key
def decrypt(ciphertext, key):
plaintext = bytes([a ^ b for a, b in zip(ciphertext, key)])
return plaintext
if __name__ == "__main__":
input_text = input("文本: ")
plaintext = input_text.encode('utf-8')
加密
ciphertext, key = encrypt(plaintext)
print(f"密文: {ciphertext.hex()}\nkey: {key.hex()}") # 输出十六进制,而不是字节
解密
decrypted_text = decrypt(ciphertext, key)
print(f"解密后的明文: {decrypted_text.decode()}")
尝试着对视频,图片,.pptx等不同格式的文件加密和解密 : )
def encryptImg(ImgPath):
# 分段保存密钥和加密内容的集合
keyList = []
ciphertextList = []
# 随机生成名字,os.urandom(3)是字节类型
imgName = os.urandom(3).hex()
try:
# 打开图片,切记用 "b"
with open(ImgPath, "rb") as f:
# 将片分段,这里是 4096一个段
for chunk in iter(lambda: f.read(4096), b""):
# 加密
ciphertext, key = encrypt(chunk)
# 保存每一段
keyList.append(key)
ciphertextList.append(ciphertext)
except Exception as e:
print(f"ERROR:{e}")
try:
# 保存加密后的图片
with open(f"image/e-{imgName}.png", "wb") as f:
for text in ciphertextList:
f.write(text)
print("SUCC OF ENCRYPT")
except Exception as e:
print(f"ERROR:{e}")
# 返回两个集合
return ciphertextList, keyList
for chunk in iter(lambda: f.read(4096), b""): 中解释如下:
f.read(4096)
:这是文件对象f
的一个方法,用于从文件中读取指定字节数的数据。在这里,4096
表示每次读取 4096 个字节的数据。如果文件剩余内容不足 4096 字节,则会读取剩余的全部内容。
lambda: f.read(4096)
:这是一个匿名函数(lambda 函数),它没有名字,功能是调用文件对象f
的read
方法并返回读取到的内容。每次调用这个 lambda 函数,就会从文件中读取 4096 字节的数据。
iter(func, sentinel)
:这是 Python 的内置函数iter
的一种特殊用法。iter
函数通常用于创建一个迭代器,这里它接收两个参数:
func
:是一个可调用对象(在这里是前面定义的 lambda 函数),每次迭代时会调用这个函数来获取下一个值。sentinel
:是一个标记值,当func
的返回值等于这个标记值时,迭代就会停止。在代码中,b""
(空字节串)被用作标记值,意味着当f.read(4096)
返回空字节串时,说明已经读取到了文件的末尾,迭代过程结束。
def decryptImg(ciphertextList, keyList, outputPath):
# 解密后的名字
imgName = os.urandom(3).hex()
try:
plaintextList = []
for ciphertext, key in zip(ciphertextList, keyList):
# 解密
plaintext = decrypt(ciphertext, key)
plaintextList.append(plaintext)
# 使用 os.path.join 正确组合路径
output_file_path = os.path.join(outputPath, f"d-{imgName}.png")
with open(output_file_path, "wb") as f:
for text in plaintextList:
f.write(text)
print("SUCC OF DECRYPT")
except Exception as e:
print(f"ERROR:{e}")
def encrypt_video(video_path):
try:
# 打开视频文件
with open(video_path, 'rb') as f:
video_data = f.read()
# 获取密钥
ciphertext, key = encrypt(video_data)
# 保存视频路径
encrypted_video_path = video_path + '.encrypted'
# 保存加密后的视频文件
with open(encrypted_video_path, 'wb') as f:
f.write(ciphertext)
# 保存密钥路径
key_path = video_path + '.key'
# 保存密钥文件
with open(key_path, 'wb') as f:
f.write(key)
# 输出保存路径
print(f"视频文件已加密并保存为 {encrypted_video_path},密钥保存为 {key_path}")
except Exception as e:
print(f"加密过程中出现错误: {e}")
def decrypt_video(encrypted_video_path, key_path):
try:
# 打开解密文件
with open(encrypted_video_path, 'rb') as f:
# 提取
encrypted_video = f.read()
# 打开密钥文件
with open(key_path, 'rb') as f:
# 提取
key = f.read()
# 解密
decrypted_video = decrypt(encrypted_video, key)
# 解密后文件保存路径
decrypted_video_path = encrypted_video_path.replace('.encrypted', '')
# 保存
with open(decrypted_video_path, 'wb') as f:
f.write(decrypted_video)
# 输出
print(f"视频文件已解密并保存为 {decrypted_video_path}")
except Exception as e:
print(f"解密过程中出现错误: {e}")
对视频和图片的处理使用了两种不同的思路,大家自己尝试。
其他格式的文件可以自己动手写一下。: >