为了帮助同学们完成痛苦的实验课程设计,本作者将其作出的实验结果及代码贴至CSDN中,供同学们学习参考。如有不足或描述不完善之处,敬请各位指出,欢迎各位的斧正!
1、掌握DES算法的工作原理。
2、熟悉分组加密算法的4种工作模式(OFB模式可不做)。
3、了解DES的雪崩效应。
Microsoft Visual Studio 2019
(1)编程实现DES算法。
(2)改变1位明文观察输出DES算法的16轮输出,几轮后密文变化达到32位以上。
(3)改变1位密钥观察输出DES算法的16轮输出,几轮后密文变化达到32位以上。
(4)在电码本模式和分组链接模式中,在最少64个分组的明文中,观察当一个密文分组错误时,还原的明文有几个分组错误。
(5)在密码反馈模式和输出反馈模式中,在最少64个分组的明文中,观察当一个密文分组错误时,还原的明文有几个分组错误。**
(1)编写一个DES算法,*输出其每一轮的加密结果并显示在屏幕上。
(2)编程实现对文件的加密,加密模式:电码本、分组链接模式;
(3)*额外要求:编程实现密码反馈模式和输出反馈模式。
说明:
(1)DES算法可以自编,也可以网上下载现成算法。
(2)四种工作模式的程序可以自编,也可以利用OpenSSL、VS的framework、cryptopp加密包编程。
(3)基本DES算法要想输出每轮的加密结果,请参照C程序;使用C#编程可以实现电码本、分组链接、密码反馈模式。
一、编写一个DES算法,输出其每一轮的加密结果并显示在屏幕上。
修改一位明文,第四轮即产生明显不同的加密结果
修改一位密文,第四轮即产生明显不同的加密结果
二、对文件的加密
明文文件:
密文文件:
解密文件:
通过修改文件后缀名得到原图
(1)程序设计的思想,及程序关键原代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.IO;
namespace SecTest
{
public partial class DesForm : Form
{
public string str1;
public string MFileNamestr1;
public string CFileNamestr;
public string MFileNamestr2;
public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF "
{
string hexString = string.Empty;
if (bytes != null)
{
StringBuilder strB = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
strB.Append(bytes[i].ToString("X2"));
}
hexString = strB.ToString();
}
return hexString;
}
public DesForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MyDES des1 = new MyDES();
if (radioButton1.Checked == true)
{
MyDES.f = 1;
}
if (radioButton2.Checked == true)
{
MyDES.f = 2;
}
if (radioButton3.Checked == true)
{
MyDES.f = 3;
}
if (radioButton5.Checked == true)
{
MyDES.ff = 1;
textBox2.Text = MyDES.Encrypt(textBox1.Text, textBox4.Text, textBox5.Text);
textBox6.Text = ToHexString(Encoding.UTF8.GetBytes(textBox2.Text));
}
if (radioButton4.Checked == true)
{
MyDES.ff = 2;
MyDES.FileEncryptor(textBox7.Text, textBox8.Text, textBox4.Text, textBox5.Text);
}
}
private void DesForm_Load(object sender, EventArgs e)
{
textBox1.Text = str1;
}
private void button2_Click(object sender, EventArgs e)
{
MyDES des1 = new MyDES();
if (radioButton1.Checked == true)
{
MyDES.f = 1;
}
if (radioButton2.Checked == true)
{
MyDES.f = 2;
}
if (radioButton3.Checked == true)
{
MyDES.f = 3;
}
if (radioButton5.Checked == true)
{
MyDES.ff = 1;
textBox3.Text = MyDES.Decrypt(textBox2.Text, textBox4.Text, textBox5.Text);
}
if (radioButton4.Checked == true)
{
MyDES.ff = 2;
MyDES.FileDecrypt(textBox8.Text, textBox9.Text, textBox4.Text, textBox5.Text);
}
}
private void button3_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
// openFileDialog.InitialDirectory = "E:\\";
// openFileDialog.Filter = "Md1 File(*.md1)|*.md1";
// openFileDialog.RestoreDirectory = true;
// openFileDialog.FilterIndex = 1;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBox7.Text = openFileDialog.FileName;
MFileNamestr1 = textBox7.Text;
}
}
private void button4_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBox8.Text = openFileDialog.FileName;
CFileNamestr = textBox8.Text;
}
}
private void button5_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBox9.Text = openFileDialog.FileName;
MFileNamestr2 = textBox9.Text;
}
}
}
public sealed class MyDES {
public static uint f;
public static uint ff;
public static bool FileDecrypt(string sInputFilename, string sOutputFilename, string key, string IV)
{
byte[] desKey = ASCIIEncoding.ASCII.GetBytes(key);
byte[] desIV = ASCIIEncoding.ASCII.GetBytes(IV);
FileStream fin = new FileStream(sInputFilename, FileMode.Open, FileAccess.Read);
FileStream fout = new FileStream(sOutputFilename, FileMode.OpenOrCreate, FileAccess.Write);
fout.SetLength(0);
long rdlen = 0;
long totlen = fin.Length;
// byte[] bin = new byte[(int)totlen];
byte[] bin = new byte[1000];
int len;
DES provider1 = new DESCryptoServiceProvider();
if (f == 1)
provider1.Mode = CipherMode.ECB;
if (f == 2)
provider1.Mode = CipherMode.CBC;
if (f == 3)
provider1.Mode = CipherMode.CFB;
CryptoStream encStream = new CryptoStream(fout, provider1.CreateDecryptor(desKey, desIV), CryptoStreamMode.Write);
while (rdlen < totlen)
{
len = fin.Read(bin, 0, 1000);
encStream.Write(bin, 0, len);
rdlen = rdlen + len;
}
encStream.Close();
fout.Close();
fin.Close();
return true;
}
public static bool FileEncryptor(string sInputFilename, string sOutputFilename, string key, string IV)
{
byte[] desKey = ASCIIEncoding.ASCII.GetBytes(key);
byte[] desIV = ASCIIEncoding.ASCII.GetBytes(IV);
FileStream fin = new FileStream(sInputFilename, FileMode.Open, FileAccess.Read);
FileStream fout = new FileStream(sOutputFilename, FileMode.OpenOrCreate, FileAccess.Write);
fout.SetLength(0);
long rdlen = 0;
long totlen = fin.Length;
byte[] bin = new byte[1000];
int len;
DES provider1 = new DESCryptoServiceProvider();
if (f == 1)
provider1.Mode = CipherMode.ECB;
if (f == 2)
provider1.Mode = CipherMode.CBC;
if (f == 3)
provider1.Mode = CipherMode.CFB;
CryptoStream encStream = new CryptoStream(fout, provider1.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
while (rdlen < totlen)
{
len = fin.Read(bin, 0, 1000);
encStream.Write(bin, 0, len);
rdlen = rdlen + len;
}
encStream.Close();
fout.Close();
fin.Close();
return true;
}
public static string Decrypt(string val, string key, string IV)
{
try {
byte[] buffer1 = ASCIIEncoding.ASCII.GetBytes(key);
byte[] buffer2 = ASCIIEncoding.ASCII.GetBytes(IV);
DESCryptoServiceProvider provider1 = new DESCryptoServiceProvider();
if(f==1)
provider1.Mode = CipherMode.ECB;
if (f == 2)
provider1.Mode = CipherMode.CBC ;
if (f == 3)
provider1.Mode = CipherMode.CFB;
provider1.Key = buffer1;
provider1.IV = buffer2;
provider1.Padding = PaddingMode.PKCS7;
ICryptoTransform transform1 = provider1.CreateDecryptor(provider1.Key, provider1.IV);
// byte[] buffer3 = Convert.FromBase64String(val);
byte[] buffer3 = Convert.FromBase64String(val);
MemoryStream stream1 = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream1, transform1, CryptoStreamMode.Write);
stream2.Write(buffer3, 0, buffer3.Length);
stream2.FlushFinalBlock();
stream2.Close();
return Encoding.Default.GetString(stream1.ToArray());
}
catch// (System.Exception ex)
{
return "";
}
}
public static string Encrypt(string val, string key, string IV) {
try {
byte[] buffer1 = ASCIIEncoding.ASCII.GetBytes(key);
byte[] buffer2 = ASCIIEncoding.ASCII.GetBytes(IV);
DESCryptoServiceProvider provider1 = new DESCryptoServiceProvider();
if (f == 1)
provider1.Mode = CipherMode.ECB;
if (f == 2)
provider1.Mode = CipherMode.CBC;
if (f == 3)
provider1.Mode = CipherMode.CFB;
KeySizes[] a = provider1.LegalBlockSizes;
provider1.Key = buffer1;
provider1.IV = buffer2;
provider1.Padding = PaddingMode.PKCS7;
ICryptoTransform transform1 = provider1.CreateEncryptor(provider1.Key, provider1.IV);
byte[] buffer3 = ASCIIEncoding.ASCII.GetBytes(val);
MemoryStream stream1 = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream1, transform1, CryptoStreamMode.Write);
stream2.Write(buffer3, 0, buffer3.Length);
stream2.FlushFinalBlock();
stream2.Close();
return Convert.ToBase64String(stream1.ToArray());
// return Convert.ToString (stream1.ToArray());
}
catch// (Exception ex)
{
return "";
}
}
}
}
//
// main.cpp
// DES
#include
#include
#include "tables.h"
#include
using namespace std;
void BitsCopy(bool *DatOut,bool *DatIn,int Len); // 数组复制
void ByteToBit(bool *DatOut,char *DatIn,int Num); // 字节到位
void BitToByte(char *DatOut,bool *DatIn,int Num); // 位到字节
void BitToHex(char *DatOut,bool *DatIn,int Num); // 二进制到十六进制 64位 to 4*16字符
void HexToBit(bool *DatOut,char *DatIn,int Num); // 十六进制到二进制
void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num); // 位表置换函数
void LoopMove(bool *DatIn,int Len,int Num); // 循环左移 Len长度 Num移动位数
void Xor(bool *DatA,bool *DatB,int Num); // 异或函数
void S_Change(bool DatOut[32],bool DatIn[48]); // S盒变换
void F_Change(bool DatIn[32],bool DatKi[48]); // F函数
void SetKey(char KeyIn[24]); // 设置密钥
void PlayDes(char MesOut[8],char MesIn[8]); // 执行DES加密
void KickDes(char MesOut[8],char MesIn[8]); // 执行DES解密
int main(){
char MesHex[16]; // 16个字符数组用于存放 64位16进制的密文
char MyKey[25]; // 初始密钥 8字节*8
char MyMessage[8]; // 初始明文
cout<<"Input Message:64bit:"<<endl;
cin>>MyMessage; // 明文
cout<<"Input Secret Key:192bit"<<endl;
cin>>MyKey; // 密钥
int len = 0;
while(MyKey[len]!='\0')
len++;
while(len!=24){
cout<<"Input Correct Secret Key!"<<endl;
cin>>MyKey;
len = 0;
while(MyKey[len]!='\0')
len++;
}
SetKey(MyKey); // 设置密钥 得到子密钥Ki
PlayDes(MesHex,MyMessage); // 执行DES加密
cout<<"Encrypting:"<<endl; // 信息已加密
for(int i = 0; i < 16; i++)
cout<<MesHex[i];
cout<<endl;
KickDes(MyMessage,MesHex); // 解密输出到MyMessage
cout<<"Deciphering:"<<endl;
for(int i = 0; i < 8; i++)
cout<<MyMessage[i];
cout<<endl;
}
//位移动
void BitsCopy(bool *DatOut,bool *DatIn,int Len){
for(int i = 0; i < Len; i++)
DatOut[i] = DatIn[i];
}
//字节转换成位
void ByteToBit(bool *DatOut,char *DatIn,int Num){
for(int i = 0; i < Num; i++)
DatOut[i] = (DatIn[i / 8] >> (i % 8)) & 0x01;
}
//位转换成字节
void BitToByte(char *DatOut,bool *DatIn,int Num){
for(int i = 0; i < (Num / 8); i++)
DatOut[i] = 0;
for(int i = 0; i < Num; i++)
DatOut[i / 8] |= DatIn[i] << (i % 8);
}
//二进制密文转换为十六进制
void BitToHex(char *DatOut,bool *DatIn,int Num){
for(int i = 0; i < Num / 4; i++)
DatOut[i] = 0;
for(int i = 0; i < Num / 4; i++){
DatOut[i] = DatIn[i * 4] + (DatIn[i * 4 + 1] << 1) + (DatIn[i * 4 + 2] << 2) + (DatIn[i * 4 + 3] << 3);
if((DatOut[i] % 16) > 9)
DatOut[i] = DatOut[i] % 16 + '7'; // 余数大于9时处理 10-15 to A-F
else
DatOut[i] = DatOut[i] % 16 + '0';
}
}
//十六进制字符转二进制
void HexToBit(bool *DatOut,char *DatIn,int Num){
for(int i = 0; i < Num; i++){
if((DatIn[i / 4]) > '9') // 大于9
DatOut[i] = ((DatIn[i / 4] - '7') >> (i % 4)) & 0x01;
else
DatOut[i] = ((DatIn[i / 4] - '0') >> (i % 4)) & 0x01;
}
}
//表置换函数
void TablePermute(bool *DatOut,bool *DatIn,const char *Table,int Num){
static bool Temp[256]={0};
for(int i = 0; i < Num; i++) // Num为置换的长度
Temp[i] = DatIn[Table[i] - 1]; // 原来的数据按对应的表上的位置排列
BitsCopy(DatOut,Temp,Num); // 把缓存Temp的值输出
}
// 子密钥的移位
void LoopMove(bool *DatIn,int Len,int Num){
static bool Temp[256]={0};
BitsCopy(Temp,DatIn,Num); // 将数据最左边的Num位(被移出去的)存入Temp
BitsCopy(DatIn,DatIn+Num,Len-Num); // 将数据左边开始的第Num移入原来的空间
BitsCopy(DatIn+Len-Num,Temp,Num); // 将缓存中移出去的数据加到最右边
}
// 按位异或
void Xor(bool *DatA,bool *DatB,int Num){
for(int i = 0; i < Num; i++)
DatA[i] = DatA[i] ^ DatB[i];
}
// S盒
void S_Change(bool DatOut[32],bool DatIn[48]){
for(int i = 0,Y = 0,X = 0; i < 8; i++,DatIn += 6,DatOut += 4){ // 每执行一次,输入数据偏移6位,输出数据偏移4位
Y = (DatIn[0] << 1) + DatIn[5]; //af代表第几行
X = (DatIn[1] << 3) + (DatIn[2] << 2) + (DatIn[3] << 1) + DatIn[4]; // bcde代表第几列
ByteToBit(DatOut,&S_Box[i][Y][X],4); // 把找到的点数据换为二进制
}
}
// F函数
void F_Change(bool DatIn[32],bool DatKi[48]){
bool MiR[48] = {0}; // 输入32位通过E选位变为48位
TablePermute(MiR,DatIn,E_Table,48);
Xor(MiR,DatKi,48); // 和子密钥异或
S_Change(DatIn,MiR); // S盒变换
TablePermute(DatIn,DatIn,P_Table,32); // P置换后输出
}
void SetKey(char KeyIn[8]){ // 设置密钥 获取子密钥Ki
static bool KeyBit[192]={0}; // 密钥二进制存储空间
static bool *KiL =&KeyBit[0],*KiR =&KeyBit[64],*KiB =&KeyBit[128];
ByteToBit(KeyBit,KeyIn,192); // 把密钥转为二进制存入KeyBit
for(int i = 0; i < 16; i++){
LoopMove(KiL,64,4);
LoopMove(KiR,64,4);
LoopMove(KiB,64,4);
bool temp[64] = {0};
for(int j = 0; j < 64; j++){
temp[j] = KeyBit[i] ^ KeyBit[i + 64];
temp[j] = temp[j] ^ KeyBit[i + 128];
}
bool tep[48] = {0};
for(int j = 0; j < 16; j++){
tep[j] = (temp[j]^temp[j+16])^(temp[j+32]^temp[j+48]);
tep[j+16] = tep[j];
tep[j+32] = tep[j];
}
for(int j = 0; j < 16; j++)
SubKey[i][j] = tep[j];
}
}
// 执行DES加密
void PlayDes(char MesOut[8],char MesIn[8]){
static bool MesBit[64]={0}; // 明文二进制存储空间 64位
static bool Temp[32]={0};
static bool *MiL=&MesBit[0],*MiR=&MesBit[32]; // 前32位 后32位
ByteToBit(MesBit,MesIn,64); // 把明文换成二进制存入MesBit
TablePermute(MesBit,MesBit,IP_Table,64); // IP置换
for(int i = 0; i < 16; i++){
BitsCopy(Temp,MiR,32); // 临时存储
F_Change(MiR,SubKey[i]); // F函数变换
Xor(MiR,MiL,32); // 得到Ri
BitsCopy(MiL,Temp,32); // 得到Li
}
TablePermute(MesBit,MesBit,IPR_Table,64);
BitToHex(MesOut,MesBit,64);
}
// 执行DES解密
void KickDes(char MesOut[8],char MesIn[8]){
static bool MesBit[64]={0}; // 密文二进制存储空间 64位
static bool Temp[32]={0};
static bool *MiL=&MesBit[0],*MiR=&MesBit[32]; // 前32位 后32位
HexToBit(MesBit,MesIn,64); // 把密文换成二进制存入MesBit
TablePermute(MesBit,MesBit,IP_Table,64); // IP置换
for(int i = 15; i >= 0; i--){
BitsCopy(Temp,MiL,32);
F_Change(MiL,SubKey[i]);
Xor(MiL,MiR,32);
BitsCopy(MiR,Temp,32);
}
TablePermute(MesBit,MesBit,IPR_Table,64);
BitToByte(MesOut,MesBit,64);
}
(2)报告DES雪崩效应的观察结果。
密钥固定,明文改变一个字节
假定密钥为11111111(00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001);
明文为12345678(00000001 00000010 00000011 00000100 00000101 00000110 00000111 00001000)。
明文变动为10345678(00000001 00000000 00000011 00000100 00000101 00000110 00000111 00001000)仅变动一位。
分析:
将原明文12345678加密后的最终结果为:
9 9 F B 3 C A 8 1 4 2 E 8 3 2 1
将变动后的明文10345678加密后的最终结果为:
2 7 8 B F B E 1 9 F 8 D 7 2 3 6
对每一轮数出的加密结果统计分析,得到每一轮的加密结果差异bit位数如下表所示:
加密轮数 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
密文相差比特数 | 1 | 4 | 15 | 31 | 33 | 33 |
加密轮数 | 7 | 8 | 9 | 10 | 11 | 12 |
密文相差比特数 | 37 | 34 | 32 | 37 | 37 | 33 |
加密轮数 | 13 | 14 | 15 | 16 | ||
密文相差比特数 | 28 | 30 | 36 | 34 |
通过统计分析,可以得出结论:DES加密具有雪崩效应。
(3)对观察结果的分析。
分析过程及结果同上:DES加密具有雪崩效应
(4)报告对电码本模式和分组链接模式中密文错误时的观察结果;并对结果进行分析。*
(5)报告对密码反馈模式和输出反馈模式中密文错误时的观察结果;并对结果进行分析。**
构造一个具备良好雪崩效应的密码或散列是至关重要的设计目标之一。若某种块密码或加密散列函数没有显示出一定程度的雪崩特性,那么它被认为具有较差的随机化特性,从而密码分析者得以仅仅从输出推测输入。这可能导致该算法部分乃至全部被破解。因此,从加密算法或加密设备的设计者角度来说,满足雪崩效应乃是必不可缺的圭臬。
这正是绝大多数块密码采用了乘积密码的原因,也是大多数散列函数使用大数据块的原因。这些特性均使得微小的变化得以通过算法的迭代迅速增殖,造成输出的每一个二进制位在算法终止前均受到输入的每一个二进制位的影响。