1.2 准备工作
全书仿真使用的计算机配置为Intel Core i7-4720HQ四核处理器(主频为2.60GHz)、32GB DDR3L 1600MHz内存、128GB SSD固态硬盘(+1TB机械硬盘)、Windows 10(64位)操作系统,使用的软件包括Eclipse C/C++(MinGW编译器)、Visual Studio 2017(社区版)、MATLAB R2016a(版本号:9.0.0.341360,64位)、Mathematica 11、Word 2017、Visio 2017和福昕PDF阅读器等。由于篇幅限制,书中给出的算法程序主要是MATLAB代码和C#代码以及部分C代码。
1.2.1 常用的灰度图像
书中使用的图像来自USC-SIPI图像库(http://sipi.usc.edu/database/)。不失一般性,仿真实验仅使用了USC-SIPI中的4个灰度图像,即Lena、Baboon、Pepper和Plane,以及全黑图像和全白图像,这些图像的大小均为256×256像素,如图1-1所示。
图1-1 仿真实验使用的图像
1.2.2 MATLAB R2016a数学软件
仿真使用了MATLAB R2016a数学软件(在实际应用中,发现版本R2016a比R2015a和R2017a的运算速度都快一些),版本号为9.0.0.341360。除了Simulink和GUI工作方式外,MATLAB主要有命令行方式和程序代码方式。本书主要使用程序代码工作方式,即编写.m文件形式的函数和程序代码。
下述程序绘制了图1-1(a)~(d)所示的图像直方图,如图1-2(a)~(d)所示。图1-2(e)、(f)分别为全黑图像直方图和全白图像直方图。
图1-2 图1-1中各图像的直方图
【程序1-1】绘制图像直方图函数myDrawHistogram.m。
【程序1-2】绘制图像直方图程序pc001.m。
程序1-1为自定义的MATLAB函数myDrawHistogram,函数以function关键字开头(第1行),函数名作为文件名,输入和输出参数均为矩阵或向量,以end关键字结尾(第21行)。第2行将输入图像x转化为double类型数据,然后按列排列成一个列向量p;第4行将图像的直方图保存在y中,并绘制直方图;第12~16行设置直方图的坐标和样式;第18~20行为函数myDrawHistogram可调用的内部函数。“%%”表示该行为分隔线,“%”表示该行为注释。
程序1-2中,第2行的clear表示清除工作区的变量,clc表示清除命令窗口显示的命令,close all表示关闭图形输出窗口;第4~7行依次读入Lena、Baboon、Pepper和Plane图像;第9~12行依次调用myDrawHistogram函数绘制Lena、Baboon、Pepper和Plane的直方图,figure(1)表示创建标号为1的图形输出窗口。
由图1-2可知,这些图像的直方图具有明显的波动特征。一般地,由图像的直方图还原出原始图像是非常困难的,而当图像具有平坦的直方图时,还原操作几乎是不可能的。
1.2.3 Eclipse C集成开发环境
Eclipse C/C++集成开发环境是目前C/C++程序设计的最佳开发平台,可以免费从官方网址(https://www.eclipse.org/)上下载安装包,接着,还要从网址(http://www.mingw.org/)上下载MinGW编译连接器安装包。然后,先安装Eclipse C/C++集成开发环境,再安装MinGW编译连接器。下面给出本书使用的C工程框架,后续算法只在该C工程框架基础上修改algr.c、algr.h和main.c文件即可。
启动Eclipse C/C++集成开发环境,选择菜单命令File|New|C Project,在弹出的对话框中输入工程名myCPFrame,选择MinGW GCC作为Toolchains,如图1-3所示。在图1-3中单击Finish按钮进入Eclipse工作窗口。
图1-3 Eclipse新建工程对话框
在Eclipse工作窗口中,通过菜单命令File|New|Source File和菜单命令File|New| Header File可分别创建源代码文件和头文件。myCPFrame工程包含的文件见表1-1。
表1-1 myCPFrame工程包含的文件
【程序1-3】 includes.h头文件。
其中,第5~9行包括了需要的系统头文件,第10~12行包括了用户自定义的头文件。
【程序1-4】 zlxdatatype.h头文件。
其中,第5~6行依次宏定义了8位无符号整型Int08U和32位无符号整型Int32U。
【程序1-5】 main.c文件。
其中,第4~5行依次宏定义图像的行数和列数;第8~10行依次定义保存明文图像、密文图像和解密后的图像的二组数组P、C和R。注意,这类变量必须定义为全局变量(作为局部变量时将导致C语言系统堆栈溢出!);第11行定义密钥K,实际密码算法中,应根据具体的算法要求变更K的定义形式,并初始化K。第13~30行为主函数main,第15行读入Lena图像数据到内存变量P;第18行调用加密函数zyEnc将P加密为C;第17、19行记录第18行的运行时间,第20行显示运行时间,单位为秒;第21行将密文图像C写入硬盘文件MyCipher.txt。第25行调用解密函数zyDec将C还原为R;第28行将解密的图像R写入硬盘文件MyRecover.txt。
在MATLAB中查看MyCipher.txt和MyRecover.txt文件,如程序1-6所示。
【程序1-6】 MATLAB下查看C程序输出的图像数据文件。
其中,第2行从文件MyCipher.txt中读入图像数据;第3行在标号为1的图形输出窗口显示密文图像C。第5~6行的工作原理与第2~3行相似,用于显示解密后的图像。
【程序1-7】 imReadWrite.h头文件。
其中,第6~7行依次声明了读图像函数imReadFile和写图像函数imWriteFile,函数中4个参数的含义依次为图像文件名imFileName、图像数据指针Arr、图像行数M和图像列数N。
【程序1-8】 imReadWrite.c文件。
其中,第4~19行为读图像数据文件函数,从图像文件imFileName中读出M行N列的图像数据,存放在二维数组Arr中。注意:这个函数要求图像数据文件中,元素间用一个空格间隔,行间用一个回车符分隔。第21~38行为写图像数据文件函数,将M行N列的二维数组Arr中的图像数据写入到文件imFileName。注意:第31行中,两个单引号间有一个空格。
【程序1-9】 algr.h头文件。
其中,第6~7行依次声明了加密函数zyEnc和解密函数zyDec,4个形参的含义依次为明文图像P或还原后的图像R、密文图像C、密钥K、图像行数M和图像列数N,这里密钥K的数据类型需根据具体的密码算法要求进行调整。
【程序1-10】 algr.c文件。
其中,zyEnc和zyDec分别为加密函数和解密函数,这里给出了一些测试代码,并没有实现具体的任务,需要结合具体的密码算法修改这两个函数。
在基于zyCPFrame工程框架进行图像加密/解密设计时,结合具体的密码算法修改main.c、algr.h和algr.c文件中相应的代码即可,因此,在后续的C工程密码算法实例中,将只给出main.c、algr.h和algr.c文件的内容。
1.2.4 Visual Studio 2017集成开发环境
Visual Studio 2017社区版是微软公司最新的程序设计平台,注册之后可长期免费使用。打开Visual Studio后,进入菜单“文件|新建|项目”,弹出如图1-4所示的界面。
图1-4 Visual Studio新建项目视图
在图1-4中,输入项目名称MyCSFrame,选择项目类型“Windows窗体应用(.NET Framework)”,单击“确定”按钮进入Visual Studio工作平台。项目MyCSFrame工作界面如图1-5所示。
图1-5 项目MyCSFrame工作界面
图1-5中,项目MyCSFrame工作界面包含的控件及其部分属性见表1-2。
表1-2 项目MyCSFrame工作界面中包含的控件及其部分属性
续表
按图1-5和表1-2设计项目工作窗体,然后向项目中添加类MyImageData.cs和MyOneTimePad.cs,得到如图1-6所示的项目文件列表(称为解决方案资源管理器)。其中,MainForm.cs是由默认创建的Form1.cs更名得到的。
图1-6 解决方案资源管理器
项目MyCSFrame实现的功能为:在cmbBoxSelectPlain(见表1-2)组合选择框中选择一幅明文图像名,共有6幅图像Lena、Baboon、Pepper、Plane、All-black和All-white可选;然后,单击Show按钮(图1-5)将显示选中的明文图像。在cmbBoxSelectMethod(见表1-2)组合选择框中选择一种图像加密算法,共有3种方法OneTimePad、TDES和AES可选;然后,单击Encrypt按钮(图1-5)将显示加密后的图像,并在txtEncTime(见表1-2)中显示加密所需的时间。接着,单击Decrypt按钮(图1-5)将显示解密后的图像,并在txtDecTime(见表1-2)中显示解密所需的时间。
项目MyCSFrame仅实现了OneTimePad加密算法,这里的OneTimePad加密算法类似于一次一密算法,需要提供两个双精度浮点数作为密钥,这两个双精度浮点数分别用作分段线性映射的初始值和控制参数。将分段线性映射循环迭代得到的状态值序列转化为8比特的伪随机字节数据流,与明文图像直接异或得到密文图像。项目MyCSFrame的运行结果如图1-7所示。在图1-7中,选择了Lena图像,密钥为0.65和0.541。
图1-7 项目MyCSFrame的运行结果
下面给出项目MyCSFrame的代码。本书后续章节的密码算法只需要在项目MyCSFrame基础上添加新的密码算法类,并将新密码算法名添加到cmbBoxSelectMethod(见表1-2)组合选择框的Items属性中,然后修改文件MainForm.cs中的方法btnEncrypt_ Click和btnDecrypt_Click,添加相应的加密与解密方法的调用代码即可。因此,后续章节中,只需要给出新的密码算法类的代码。
【程序1-11】 MainForm.cs文件。
在程序1-11中,第14~19行的方法MainForm_Load在窗体MainForm装入时(程序主界面显示前)被调用,第16~17行设置Encrypt和Decrypt命令按钮不可用,第18行调用方法KeyReadOnly将Secret Keys区域中的16个文本框设为只读属性。
第39行创建MyImageData类的实例myImageData;第40行创建MyOneTimePad类的实例myOneTimePad。
第41~52行为Show命令按钮的单击事件,当组合框cmbBoxSelectPlain为非空时,即选择了某个明文(第43行为真),第45~46行从硬盘中读入该图像,该图像存放在工程编译链接得到的可执行程序文件所在目录下的myImages子目录下,共有6幅明文图像,即Lena.tif、Baboon.tif、Pepper.tif、Plane.tif、All-black.tif和All-white.tif。第47行在picBoxPlain图像框中显示选中的图像。第50行使得Encrypt命令按钮可用。
第53~62行为组合框cmbBoxSelectMethod选择不同加密算法时触发的方法。如果选取了OneTimePad(第57行为真),则Secret Keys区域中的前2个文本框可以输入文本,如图1-7所示,依次输入了0.65和0.541,分别用作分段线性映射的初始值和控制参数。
第63~93行为Encrypt命令按钮的单击事件。如果当前选中的加密算法为OneTimePad(第65行为真),则第70~71行从文本框txtKey01和txtKey02中读取分段线性映射的初始值x0和参数p,当x0和p合法(第72行为真)时,第74行从对象myImageData中读取明文数据(myImageData对象中保存了明文、密文和解密后的图像),第75~76、79~80行统计第77~78行语句的运行时间,第77行调用动态方法MyRandomGen产生密码流,第78行调用方法MyEncrypt实现加密处理,第81行在文本框txtEncTime中显示加密耗费的时间,如图1-7所示,第82行将密文图像保存到对象myImageData中,第83~84行在picBoxCipher图像框中显示密文图像,第85行使Decrypt命令按钮可用。
第94~122行为Decrypt命令按钮的单击事件。其中,第107行生成解密用的密码流,第108行调用方法MyDecrypt进行解密处理,第111行在文本框txtDecTime中显示解密耗费的时间,第112行将解密后的图像保存到myImageData对象中,第113~114行在picBoxRecovered图像框中显示还原后的图像。
【程序1-12】 MyImageData.cs文件。
程序1-12所示的MyImageData.cs文件中定义了类MyImageData,该类中定义了2个私有数据成员行数height和列数width(第8~9行),分别用于保存图像的行数和列数;第13~26的属性Height用于存取height,第27~40行的属性Width用于存取width。第10~12行依次定义了3个二维数组PlainImage、CipherImage和RecoveredImage,分别用于保存明文图像、密文图像和解密后的图像。
第41~54行的方法MyGetPlainImage用于将图像bmp中的数据读到二维数组PlainImage中。需要注意的是,在第50行的GetPixel方法参数中,两个形参依次为列数和行数(即列在前,行在后),返回值为当前位置像素点的颜色。第51行将读出的像素点的颜色的R分量(即红色分量)赋给PlainImage[i,j]。这是因为明文图像为灰度图像,读出的像素点的颜色的三个分量(R、G和B分量)均相等,可任取一个分量,或者取三者的平均值。
第55~70行为由密文矩阵生成密文图像的方法MyShowCipherImage。第57行创建Bitmap实例bitMap,第60~68行为二重循环体,将二维数组CipherImage的元素填充到bitMap对象中。
第71~86行为由解密后的矩阵数据生成解密后的图像的方法MyShowRecoveredImage,工作原理与方法MyShowCipherImage相似。第73行创建Bitmap实例bitMap,第76~84行为二重循环体,将二维数组RecoveredImage的元素填充到bitMap对象中。
【程序1-13】 MyOneTimePad.cs文件。
在C#工程中,每种密码算法都对应一个类,保存在一个单独的.cs文件中。一般地,类名和文件名相同。在文件MyOneTimePad.cs中定义的类MyOneTimePad实现了类似一次一密的密码算法。第11~14行依次定义了私有成员plainImage、cipherImage、recoveredImage和randomDat,分别用于临时保存明文图像、密文图像、解密后的图像和密码矩阵。
第15~20行的方法setPlainImage用于从对象myImDat中获取明文图像。第21~26行的方法getCipherImage将加密得到的密文图像cipherImage赋给对象myImDat中的成员CipherImage。第27~32行的方法getRecoveredImage将解密后的图像recoveredImage赋给对象myImDat的成员RecoveredImage。
第33~47行为生成密码矩阵的方法MyRandomGen,密码流保存在二维数组randomDat中。第48~56行的方法PWLCM为分段线性混沌映射,输入的形参x0和p分别为迭代初始值和控制参数。
第57~68行为加密方法MyEncrypt,这里将密码矩阵randomDat与明文图像矩阵plainImage相异或得到密文矩阵cipherImage。第69~80行为解密方法MyDecrypt,是加密方法MyEncrypt的逆运算,即将密码矩阵randomDat与密文矩阵cipherImage相异或得到还原后的图像矩阵recoveredImage。