一、作品概述
1.1 当前图像加密算法研究现状
图像加密算法研究的主要包括对称加密算法、非对称加密算法、混沌加密算法、基于图像特征的加密算法和量子图像加密算法等。对称加密算法包括DES、3DES、AES等,加密速度快但需要保证密钥的安全性;非对称加密算法包括RSA、DSA、ECC等,密钥的安全性更高但加密速度较慢。混沌加密算法利用混沌系统的随机性和复杂性来实现加密,能够提高加密的安全性但加密速度较慢。基于图像特征的加密算法利用图像的特征来实现加密,提高了加密的效率和安全性。量子图像加密算法,利用量子态的特性进行加密和解密,提供了更高的安全性。但目前,量子图像加密算法还处于研究和探索阶段,需要解决量子比特的处理和传输的技术挑战。
1.2 本作品加密算法简介
本作品是基于密码本映射加密及一次一密加密方案实现的,运用了二者的加密思想。
1.2.1 密码本映射加密
密码本映射加密是一种基于密码本的映射加密方法。它使用一个密码本或密码盘来定义字符之间的映射关系,从而实现加密和解密过程。
加密时,将明文字符对应的密文字符找到并输出,形成密文。解密时,根据密码本中的映射关系,将密文字符还原为明文字符。
1.2.2 一次一密加密(OTP)
一次一密加密是一种基于密钥流的加密方法,也被称为OTP加密。它提供了理论上的完美保密性。One-Time Pad是一种基于密钥的对称加密算法,其安全性建立在密钥的随机性和保密性之上。
在一次一密加密中,密钥流是与明文长度相等的随机二进制序列。明文通过与密钥流进行异或运算来生成密文,解密时再次与密钥流进行异或运算就能够还原出明文。密钥流只能使用一次,之后应该销毁,因此被称为"一次一密"。
由于一次一密加密使用了真正的随机密钥流,并且在每次加密时都使用一个新的密钥流,所以它提供了理论上的完美保密性。即使攻击者拥有无限计算资源,也无法从密文中获取任何有用的信息
二、作品设计与实现
本作品是以密码本为密钥基础进行的,实现了使用密码本映射加密和One-Time Pad进行图像加密和解密的功能。首先使用了一个自定义的密钥(不限于学号)来生成随机映射和随机OTP密钥流,并形成加密密码本和解密密码本。
可以说只要密码本不泄露,即密钥流不泄露,该方案是无法破解的。接下来通过解释密码本生成原理、映射加密原理、OTP加密原理和加解密流程来说明作品:
2.1 加密与解密原理
2.1.1 密码本生成原理
使用自定义密钥(这里包括但不限于学号)作为种子,通过伪随机数生成器生成一个0到255之间数字的随机排列。因为像素点是0--255之间的整数,所以将像素点映射,让像素点排列混乱。生成的随机排列被用作加密密码本和解密密码本,其中加密密码本将明文值映射到对应的密文值,解密密码本将密文值映射回明文值。
并随机生成一个大于原始图像像素点数量的随机OTP密钥流,将随机密钥流作为一个额外的键值对存储在加密和解密密码本中,密码本生成完毕,只要妥善保存,就能对图像进行加解密。
映射密码由于用自定义密钥做种子生成,故自定义相同,生成的密码本相同,但是OTP密钥流是每次均是随机的,即不可能一样,满足一次一密,都使用一个唯一的密钥,这个密钥只使用一次,不会重复。
2.1.2 映射加密原理(解密为逆过程)
在图像加密过程中,将灰度图像中的每个像素值作为密码本的键,通过加密字典将其映射到对应的密文值。通过遍历图像的每个像素,找到对应的密文值,并用密文值替换原始像素值。这样,图像中的每个像素值都经过了替换,实现了加密操作。
2.1.3 一次一密加密原理(解密为逆过程)
One-Time Pad加密算法的实现是基于一个随机密钥矩阵。随机密钥矩阵是一个与原始图像具有相同大小的矩阵,其中的每个元素都是0到255之间的随机整数,即矩阵中的每个元素都是一个随机生成的8位无符号整数。加密过程中,每个像素值都与密钥矩阵中对应位置的元素进行加运算,并对256取模,从而得到加密图像的像素值。
对于每个映射加密后的像素值,使用OTP密钥进行模加运算(np.add),然后再对256取模(np.mod),得到最终的加密后的像素值。
这样,一次一密的随机OTP密钥为每个像素提供了额外的加密层,增加了加密的复杂性和安全性。
总体来说,密码本生成原理是通过生成随机排列来创建加密和解密密码本,映射加密原理是将明文像素值通过加密密码本映射为密文值,一次一密加密原理是使用一次性密钥对加密后的图像进行进一步加密。这些原理相结合,实现了本作品图像的加密和解密过程。
加解密原理的核心在于使用加密密码本进行映射加密,同时通过一次一密的随机OTP密钥增加了加密的复杂性和安全性。加密过程将明文像素值映射为密文值,并与OTP密钥相加;解密过程使用解密字典将密文值映射回明文值,并与OTP密钥相减。这样的加解密过程实现了图像的保密性和恢复性,确保只有持有正确密钥的人能够解密并查看原始图像。
2.2 方案流程图
将原始灰度图像中的像素值映射为新的像素值,从而实现加密。整个过程中,随机映射和随机密钥都是动态生成的,保证了加密的安全性。
解密为逆过程,先进行OTP解密,再进行映射解密,这里不做流程图绘制。
2.3 作品实现——用户操作界面
2.3.1 密码本生成
2.3.2 加密界面
程序运行
加密图像
2.3.3 解密界面
程序运行
解密图像
三、方案测试与分析
3.1 加解密正确性测试
以学号为**********,生成的密码本为
对Lena加密后的图像为:
对密文图像解密后的结果为:
3.2 密钥敏感性测试
学号为**********,用学号***********+1 对Lena图像加密后的结果分别为:
可见两种情况下密文图像的肉眼看不出差别。
3.3 直方图测试
原始图像的直方图为:
加密后的直方图为:
每个像素值都出现的次数差不多,攻击者难以从加密后的图像中推断出原始图像的内容。这是因为图像中每个像素值的出现频率都是相同的,不会出现某些像素值明显更多或更少的情况。
3.4 算法运行时间分析
使用提出的算法,加密一次图像的运行时间为:
使用提出的算法,解密一次图像的运行时间为:
使用DES/AES算法,加密一次图像的运行时间为:0.030901799999999746秒
使用DES/AES算法,解密一次图像的运行时间为:0.030882899999999267 秒
因为进行了两轮加密,以确保数据的安全性,故比传统的慢一点,但换来的是极高的安全性
3.5 唯密文攻击和选择明文攻击分析
3.5.1 唯密文攻击分析
使用OTP加密,每个明文值都使用一个唯一的随机OTP密钥进行加密。对于唯密文攻击来说,攻击者只能通过已知的密文来尝试推断明文。由于OTP密钥是随机生成的且只使用一次,无法通过已知的密文推断出任何关于明文或密钥的信息。因此,该算法可以防止攻击者通过已知的密文获取有用的信息。
3.5.2 选择明文攻击分析
选择明文攻击是攻击者能够选择明文并获取相应密文的攻击方式。在该算法中,加密密码本是基于随机映射生成的,并且用户可以自定义密钥。通过随机映射和自定义密钥,每个明文值都会被映射到一个唯一的密文值,且不同的用户具有不同的加密字典和密钥。这样,攻击者无法通过选择明文来获取加密字典或密钥的有用信息,从而无法推断其他明文的密文。
3.6 雪崩效应分析
本作品主要涉及到两个步骤可能产生雪崩效应的地方:生成随机映射和生成一次一密的随机OTP密钥。
在生成随机映射中,使用自定义密钥作为种子生成一个随机排列,将0到255之间的数字进行随机化排列。这种随机排列的生成机制可以保证输入数据的微小变化,例如自定义密钥的微小变化,将会导致生成的随机映射发生巨大变化。
在生成一次一密的随机OTP密钥中,使用numpy的随机数生成器生成一个随机密钥矩阵,其中的每个元素都是0到255之间的随机整数。每次都会导致生成的随机OTP密钥完全不同。
雪崩效应增加了一次性密码本加密算法的安全性,确保了即使密钥发生微小变化,加密结果也会发生巨大变化。
四、创新性说明
1. 自定义密钥和随机映射。通过用户提供的自定义密钥,结合随机映射的生成,确保了每个用户生成的加解密密码本都是唯一的,增强了算法的安全性。
2. 使用一次性密码本(OTP)加密算法。OTP是一种理论上具有完美保密性的加密算法。它使用与明文长度相等的随机密钥进行加密,确保每个密钥只使用一次。在这个算法中,通过生成随机的OTP密钥并将其与明文进行模加运算,实现了一次性加密。
3. 结合了映射加密和一次一密。将一次性密码本加密算法与映射加密和图像处理相结合,提供了一种安全、高效的图像加密和解密方案。同时,通过自定义密钥和随机映射的生成,增加了算法的安全性和灵活性。只要密码本不泄露,即密钥流不泄露,该方案是无法破解的。适用于各种图像加密和解密的应用场景。
4.该方案不仅适用于灰度图的加解密,对于文本加密、彩色图加密,只要对代码略加修改即可,且要加密的数据越多,该算法越安全,因为一次一密的密钥流越长,更能保证他的不可破解性。
五、附录(关键步骤算法代码)
生成密码本:
import numpy as np
import random
# 自定义密钥
key = **********
# 将密钥转化为字符串
key_str = str(key)
# 生成随机映射
random.seed(int(key_str))
data_list = list(range(256))
random.shuffle(data_list)
# 生成加密字典和解密字典
encrypt_dict = {}
decrypt_dict = {}
for i in range(256):
decrypt_dict[data_list[i]] = i
encrypt_dict[i] = data_list[i]
# 生成随机密钥
otp_key = np.random.randint(0, 256, size=(512,512), dtype=np.uint8)
# 将随机密钥作为一个额外的键值对存储在加密和解密字典中
encrypt_dict["otp_key"] = otp_key.tolist()
decrypt_dict["otp_key"] = otp_key.tolist()
# 将字典保存到文件
print(encrypt_dict)
print(decrypt_dict)
np.save("encrypt_dict.npy", encrypt_dict)
np.save("decrypt_dict.npy", decrypt_dict)
加解密代码
import numpy as np
from PIL import Image
#加载密码本
code=np.load('encrypt_dict.npy',allow_pickle=True).item()
# 读取一次一密随机密钥
otp_key = np.array(code["otp_key"])
im = Image.open('./1.png').convert('L') # 读取灰度图像
im2 = np.array(im)
size1 = im2.shape
new_array=np.zeros(size1)
for i in range(0,size1[0]):
for j in range(0,size1[1]):
# 对于灰度图像,只有一个通道
new_array[i, j] = code[im2[i, j]]
tmp_im = Image.fromarray(np.uint8(new_array))
# 读取图像
im_array = np.array(tmp_im.convert('L'))
# 对原始图像进行One-Time Pad加密
encrypted_array = np.mod(np.add(im_array, otp_key), 256).astype(np.uint8)
# 将原始灰度图像和随机密钥转换为16位无符号整数,然后相加,并对256取模,最后转换回8位无符号整数,得到加密后的像素值
# 这里使用numpy的mod函数实现对256取模
# 将加密后的图像保存到文件
encrypted_im = Image.fromarray(encrypted_array)
# 保存加密后的图片
encrypted_im.save('encrypted_image.png')
# 加载解密用的字典
solve_code = np.load('decrypt_dict.npy', allow_pickle=True).item()
# 读取一次一密随机密钥
otp_key = np.array(solve_code["otp_key"])
# 读取加密后的图像
encrypted_im = Image.open('encrypted_image.png').convert('L')
encrypted_array = np.array(encrypted_im)
# 对加密后的图像进行one time pad解密
decrypted_array = np.mod(np.subtract(encrypted_array, otp_key), 256).astype(np.uint8)
# 将加密后的图像和随机密钥转换为16位无符号整数,然后相减,并对256取模,最后转换回8位无符号整数,得到解密后的像素值
# 这里同样使用numpy的mod函数实现对256取模
# 将解密后的图像保存到文件
decrypted_im = Image.fromarray(decrypted_array)
im2 = np.array(decrypted_im.convert('L'))
# 解密图像
size1 = im2.shape
new_array = np.zeros(size1)
for i in range(0, size1[0]):
for j in range(0, size1[1]):
new_array[i, j] = solve_code[im2[i, j]]
# 显示解密后的图像,并保存
im3 = Image.fromarray(np.uint8(new_array))
im3.save('flag3.png')
decrypt_image(含用户界面)
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk, Image
import numpy as np
from PIL import Image
root = tk.Tk()
root.title("图像解密程序")
def browse_file():
filepath = filedialog.askopenfilename()
file_path_entry.delete(0, tk.END)
file_path_entry.insert(0, filepath)
# 显示选择的图像
image = Image.open(filepath)
image.thumbnail((300, 300))
photo = ImageTk.PhotoImage(image)
original_image_label.config(image=photo)
original_image_label.image = photo
def browse_codebook():
filepath = filedialog.askopenfilename()
codebook_path_entry.delete(0, tk.END)
codebook_path_entry.insert(0, filepath)
def browse_output_path():
output_path = filedialog.askdirectory()
output_path_entry.delete(0, tk.END)
output_path_entry.insert(0, output_path)
def decrypt_image():
filepath = file_path_entry.get()
codebook_path = codebook_path_entry.get()
output_path = output_path_entry.get()
# 加载解密用的字典
solve_code = np.load(codebook_path, allow_pickle=True).item()
# 读取一次一密随机密钥
otp_key = np.array(solve_code["otp_key"])
# 读取加密后的图像
encrypted_im = Image.open(filepath).convert('L')
encrypted_array = np.array(encrypted_im)
# 对加密后的图像进行one time pad解密
decrypted_array = np.mod(np.subtract(encrypted_array, otp_key), 256).astype(np.uint8)
# 将解密后的图像保存到文件
decrypted_im = Image.fromarray(decrypted_array)
im2 = np.array(decrypted_im.convert('L'))
#随机密码本映射解密
size1 = im2.shape
new_array = np.zeros(size1)
for i in range(0, size1[0]):
for j in range(0, size1[1]):
new_array[i, j] = solve_code[im2[i, j]]
# 显示解密后的图像,并保存
im3 = Image.fromarray(np.uint8(new_array))
# 保存解密后的图像
output_path = output_path + "/decrypted_image.png"
im3.save(output_path)
# 显示解密后的图像
image = Image.open(output_path)
image.thumbnail((300, 300))
photo = ImageTk.PhotoImage(image)
decrypted_image_label.config(image=photo)
decrypted_image_label.image = photo
# 创建选择加密图像按钮
file_browse_button = tk.Button(root, text="选择解密图像", command=browse_file)
file_browse_button.grid(row=0, column=0, padx=10, pady=10)
# 创建加密图像路径文本框
file_path_entry = tk.Entry(root, width=40)
file_path_entry.grid(row=0, column=1, padx=10, pady=10)
# 创建选择密码本按钮
codebook_browse_button = tk.Button(root, text="选择解密密码本", command=browse_codebook)
codebook_browse_button.grid(row=1, column=0, padx=10, pady=10)
# 创建密码本路径文本框
codebook_path_entry = tk.Entry(root, width=40)
codebook_path_entry.grid(row=1, column=1, padx=10, pady=10)
# 创建选择输出路径按钮
output_path_browse_button = tk.Button(root, text="选择输出路径", command=browse_output_path)
output_path_browse_button.grid(row=2, column=0, padx=10, pady=10)
# 创建输出路径文本框
output_path_entry = tk.Entry(root, width=40)
output_path_entry.grid(row=2, column=1, padx=10, pady=10)
# 创建解密按钮
decrypt_button = tk.Button(root, text="解密", command=decrypt_image)
decrypt_button.grid(row=3, column=1, padx=10, pady=10)
# 创建框架来放置原始图像和解密后的图像
image_frame = tk.Frame(root)
image_frame.grid(row=4, column=0, columnspan=2, padx=10, pady=10)
# 创建标签来显示原始图像
original_image_label = tk.Label(image_frame)
original_image_label.grid(row=0, column=0, padx=10, pady=10)
# 创建标签来显示解密后的图像
decrypted_image_label = tk.Label(image_frame)
decrypted_image_label.grid(row=0, column=1, padx=10, pady=10)
root.mainloop()
encrypt_image(含用户界面)
import tkinter as tk
import numpy as np
from PIL import Image
from tkinter import filedialog
from PIL import ImageTk, Image
def browse_file():
filepath = filedialog.askopenfilename()
file_path_entry.delete(0, tk.END)
file_path_entry.insert(0, filepath)
# 显示选择的图像
image = Image.open(filepath)
image.thumbnail((300, 300))
photo = ImageTk.PhotoImage(image)
original_image_label.config(image=photo)
original_image_label.image = photo
def browse_codebook():
filepath = filedialog.askopenfilename()
codebook_path_entry.delete(0, tk.END)
codebook_path_entry.insert(0, filepath)
def browse_output_path():
output_path = filedialog.askdirectory()
output_path_entry.delete(0, tk.END)
output_path_entry.insert(0, output_path)
def encrypt_image():
filepath = file_path_entry.get()
codebook_path = codebook_path_entry.get()
output_path = output_path_entry.get()
# 在这里添加加密图像的代码
#加载加密密码本
code=np.load(codebook_path,allow_pickle=True).item()
# 读取一次一密随机密钥
otp_key = np.array(code["otp_key"])
# 读取灰度图像
im = Image.open(filepath).convert('L')
im2 = np.array(im)
size1 = im2.shape
new_array=np.zeros(size1)
#随机密码本映射加密
for i in range(0,size1[0]):
for j in range(0,size1[1]):
new_array[i, j] = code[im2[i, j]]
tmp_im = Image.fromarray(np.uint8(new_array))
# 对图像再进行One-Time Pad加密
im_array = np.array(tmp_im.convert('L'))
encrypted_array = np.mod(np.add(im_array, otp_key), 256).astype(np.uint8)
# 保存加密后的图片
encrypted_im = Image.fromarray(encrypted_array)
output_path = output_path + "/encrypted_image.png"
encrypted_im.save(output_path)
# 显示加密后的图像
image = Image.open(output_path)
image.thumbnail((300, 300))
photo = ImageTk.PhotoImage(image)
encrypted_image_label.config(image=photo)
encrypted_image_label.image = photo
root = tk.Tk()
root.title("图像加密程序")
# 创建选择文件按钮
file_browse_button = tk.Button(root, text="选择初始图像", command=browse_file)
file_browse_button.grid(row=0, column=0, padx=10, pady=10)
# 创建文件路径文本框
file_path_entry = tk.Entry(root, width=40)
file_path_entry.grid(row=0, column=1, padx=10, pady=10)
# 创建选择密码本按钮
codebook_browse_button = tk.Button(root, text="选择加密密码本", command=browse_codebook)
codebook_browse_button.grid(row=1, column=0, padx=10, pady=10)
# 创建密码本路径文本框
codebook_path_entry = tk.Entry(root, width=40)
codebook_path_entry.grid(row=1, column=1, padx=10, pady=10)
# 创建选择输出路径按钮
output_path_browse_button = tk.Button(root, text="选择输出路径", command=browse_output_path)
output_path_browse_button.grid(row=2, column=0, padx=10, pady=10)
# 创建输出路径文本框
output_path_entry = tk.Entry(root, width=40)
output_path_entry.grid(row=2, column=1, padx=10, pady=10)
# 创建加密按钮
encrypt_button = tk.Button(root, text="加密", command=encrypt_image)
encrypt_button.grid(row=3, column=1, padx=10, pady=10)
# 创建框架来放置原始图像和加密后的图像
image_frame = tk.Frame(root)
image_frame.grid(row=4, column=0, columnspan=2, padx=10, pady=10)
# 创建标签来显示原始图像
original_image_label = tk.Label(image_frame)
original_image_label.grid(row=0, column=0, padx=10, pady=10)
# 创建标签来显示加密后的图像
encrypted_image_label = tk.Label(image_frame)
encrypted_image_label.grid(row=0, column=1, padx=10, pady=10)
root.mainloop()