密码学的期末大作业:基于一种数学难题,实现对信息的加密与解密,并采用一种摘要算法生成摘要,并对摘要进行加密
采用的语言为 C/C++
数学难题为 背包算法(详细原理可看:背包算法原理写的很好,容易理解)
摘要算法采用的是MD5(copy的,因为大作业中并未对摘要算法有要求,于是就从简copy了一份)
在对摘要进行加解密时,由于背包问题逆向为数学难题,无法直接反过来,所以采用了一些特殊条件
基于背包问题,实现了加密解密过程,可对由字母,数字和空格组成的字符串进行加密解密。在此基础上进行了数字签名。采用了MD5 摘要算法,并对摘要进行加密传输,解密后认证相同即为认证成功。
背包利用超递增序列作私钥,加密时通过两个互素数M,N,构造不规则序列公钥,将明晚进行二进制转化后对应求和得到密文。解密时先求出N的逆元后,对密文进行取模,再对照私钥进行解密,最后得到明文。
签名时首先通过MD5算法对明文进行摘要,并用私钥对摘要进行加密。解密时先将解密到的明文进行摘要,再对被私钥加密的摘要进行解密,通过已知公钥,M,N,求出私钥,进行解密。将解密到的摘要与解密明文提取的摘要进行对比,相同即可证明该信息可靠。
通过打表的方式将数字与英文字母进行对应转换,可将其转化为六位二进制数字
string mi[37] = {"000000","000001","000010","000011","000100",
"000101","000110","000111","001000","001001",
"001010","001011","001100","001101","001110",
"001111","010000","010001","010010","010011",
"010100","010101","010110","010111","011000",
"011001","011010","011011","011100","011101",
"011110","011111","100000","100001","100010",
"100011","100100"};
char mm[37] = {' ','a','b','c','d',
'e','f','g','h','i',
'j','k','l','m','n',
'o','p','q','r','s',
't','u','v','w','x',
'y','z','0','1','2',
'3','4','5','6','7',
'8','9'};
变量声明:
int A[12] = {1,3,5,11,21,44,87,175,349,701,1399,2798}; //私钥
int m; //= 5598;
int n;
int B[12];//公钥
int sum[3000];//加密后的信息
int sum1[3000];//加密后的摘要
int ans[3000];//转换后的信息
int ans1[3000];//转换后的摘要
int kk;//加密时分组的个数
int kkz = 0;//摘要分组的个数
string ming; //输入的明文
string zhai,zhai1,zhai2;//摘要
string zu[3000];//加密分组后的信息
string zuz[3000];//摘要分组后的信息
string answer,answer1;//解密后的明文
string flag[3000];//转换后的串
string flagz[3000];//转换后的串
Ready():初始化函数
输出默认给定的私钥
//私钥
int sum = 0;
cout << "私钥为:[ ";
for(int i=0; i<12; i++){
sum += A[i];
cout << A[i] << " ";
if(i!=11) cout << ",";
}
cout <<" ]"<< endl;
cout << endl;
生成M
M的要求为:比私钥A中数字的和大
//随机选取m
srand((int)time(0));
while(1){
int h = random(10);
if(h) {
m = sum + h;
break;
}
}
生成N
N的要求为:与M互为素数
srand((int)time(0));
while(1){
int k = random(100);
if(gcd(m,k)==1 && k != 1){
n = k;
break;
}
}
//最大公约数函数
int gcd(int a ,int b){
return b==0 ? a : gcd(b, a%b);
}
生成公钥B并输出:
公钥B是由私钥A生成,b=a*n%m
//生成公钥
for(int k=0; k<12; k++){
B[k] = (A[k]*n)%m;
}
cout << "生成的公钥为:[ ";
for(int i=0; i<12; i++){
cout << B[i] << " ";
if(i!=11) cout << ",";
}
cout <<" ]"<< endl;
cout << endl;
cout << "随机选取的两个互素数为:" << m << " " << n << endl;
cout << endl;
明文的读入:
明文存储在文件1.txt中,通过freopen()读入
ready();
cout << "请输入要加密的明文:" <
函数名称:jiami()
第一部分:将明文转化成为对应六位二进制数
当输入的明文数量为单数时,会自动补零
int l = ming.length();
string zu1;
//int kk = 0;
for(int k=0; k
第二部分:将对应的二进制数字与公钥相对应
‘1’位的数字相加得到密文,每两个字符为一组,最后生成传输的密文,打印到屏幕上
for(int i=0; i
解密部分默认已知私钥A,M,N
第一部分:求出N的逆元
利用辗转相除法求出N的逆元
int ni(int m,int n){
int a, q, b[100], my;
q = 0;
a = m%n;
my = m;
while(a!=0){
a = m%n;
b[q] = m/n;
q++;
m = n;
n = a;
}
int bb[100], j=2, bbb;
bb[0]=1;
bb[1] = b[q-2];
int nn;
nn=q-2;
for(int i=nn; i>0; i--){
bb[j] = b[i-1]*bb[j-1] + bb[j-2];
bbb = bb[j];
j++;
}
if((q-1)%2==0) return(bbb);
else return (my-bbb);
}
第二部分:通过逆元还原密文
将密文的每个值与逆元相乘再模M,再将得到的数字与私钥A相比,还原出六位二进制字符串
//已知私钥A,m,n
if(sum[0]==0){
cout << "请先进行加密!" << endl;
}
int niyuan = ni(m,n);
for(int i=0; i=0; j--){
if(ans[i]>=A[j]){
flag1 += "1";
ans[i] = ans[i] - A[j];
}
else flag1 += "0";
}
reverse(flag1.begin(),flag1.end());
flag[i] = flag1;
}
第三部分:将得到的二进制与转换表对应进行转换,得到明文
利用string的翻转函数,截取函数等,注意解密后去除末尾的空格
(由加密时补位的0造成),否则会导致后面的签名,摘要部分出现误差。
for(int i=0; i
摘要部分采用的算法为MD5算法,详细代码见下面代码部分。
第一部分:与明文加密部分向同,将摘要进行二进制数字转换
int l = ming.length();
string zu1;
for(int k=0; k
第二部分:与明文加密类似,但是用的是私钥
for(int i=0; i
由于背包算法逆过程为数学难题,无法直接反推,所以采取用私钥加密,解密部分默认已知公钥B,M,N,反推回私钥A并进行解密的方法。
第一部分:私钥A进行还原
//已知公钥B,m,n
answer1 = "";
int BB[12];
int niyuan = ni(m,n);
for(int i=0; i<12; i++){
BB[i] = B[i]*niyuan % m;
}
第二部分:将密文与私钥A进行对比还原二进制数字串
for(int i=0; i=0; j--){
if(sum1[i]>=BB[j]){
flag1 += "1";
sum1[i] = sum1[i] - BB[j];
}
else flag1 += "0";
}
reverse(flag1.begin(),flag1.end());
flagz[i] = flag1;
}
第三部分:将二进制数字串与转换表对应,还原出摘要
for(int i=0; i
通过调用上面的加解密,签名函数等实现整个项目,并对两个摘要进行对比验证,给出验证结果:
int main(){
ready();
cout << "请输入要加密的明文:" <
#include
#include
#include
#include
#include
#include
#include
#define random(x) (rand()%x)
using namespace std;
int A[12] = {1,3,5,11,21,44,87,175,349,701,1399,2798}; //私钥
int m; //= 5598;
int n;
int B[12];//公钥
int sum[3000];//加密后的信息
int sum1[3000];//加密后的摘要
int ans[3000];//转换后的信息
int ans1[3000];//转换后的摘要
int kk;//加密时分组的个数
int kkz = 0;//摘要分组的个数
string ming; //输入的明文
string zhai,zhai1,zhai2;//摘要
string zu[3000];//加密分组后的信息
string zuz[3000];//摘要分组后的信息
string answer,answer1;//解密后的明文
string flag[3000];//转换后的串
string flagz[3000];//转换后的串
string mi[37] = {"000000","000001","000010","000011","000100",
"000101","000110","000111","001000","001001",
"001010","001011","001100","001101","001110",
"001111","010000","010001","010010","010011",
"010100","010101","010110","010111","011000",
"011001","011010","011011","011100","011101",
"011110","011111","100000","100001","100010",
"100011","100100"};
char mm[37] = {' ','a','b','c','d',
'e','f','g','h','i',
'j','k','l','m','n',
'o','p','q','r','s',
't','u','v','w','x',
'y','z','0','1','2',
'3','4','5','6','7',
'8','9'};
//最大公约数函数
int gcd(int a ,int b){
return b==0 ? a : gcd(b, a%b);
}
//辗转相除法求逆元
int ni(int m,int n){
int a, q, b[100], my;
q = 0;
a = m%n;
my = m;
while(a!=0){
a = m%n;
b[q] = m/n;
q++;
m = n;
n = a;
}
int bb[100], j=2, bbb;
bb[0]=1;
bb[1] = b[q-2];
int nn;
nn=q-2;
for(int i=nn; i>0; i--){
bb[j] = b[i-1]*bb[j-1] + bb[j-2];
bbb = bb[j];
j++;
}
if((q-1)%2==0) return(bbb);
else return (my-bbb);
}
void ready(){
//私钥
int sum = 0;
cout << "私钥为:[ ";
for(int i=0; i<12; i++){
sum += A[i];
cout << A[i] << " ";
if(i!=11) cout << ",";
}
cout <<" ]"<< endl;
cout << endl;
//随机选取m
srand((int)time(0));
while(1){
int h = random(10);
if(h) {
m = sum + h;
break;
}
}
srand((int)time(0));
while(1){
int k = random(100);
if(gcd(m,k)==1 && k != 1){
n = k;
break;
}
}
//生成公钥
for(int k=0; k<12; k++){
B[k] = (A[k]*n)%m;
}
cout << "生成的公钥为:[ ";
for(int i=0; i<12; i++){
cout << B[i] << " ";
if(i!=11) cout << ",";
}
cout <<" ]"<< endl;
cout << endl;
cout << "随机选取的两个互素数为:" << m << " " << n << endl;
cout << endl;
}
//加密部分
void jiami(string ming){
int l = ming.length();
string zu1;
//int kk = 0;
for(int k=0; k=0; j--){
if(ans[i]>=A[j]){
flag1 += "1";
ans[i] = ans[i] - A[j];
}
else flag1 += "0";
}
reverse(flag1.begin(),flag1.end());
flag[i] = flag1;
}
for(int i=0; i=0; j--){
if(sum1[i]>=BB[j]){
flag1 += "1";
sum1[i] = sum1[i] - BB[j];
}
else flag1 += "0";
}
reverse(flag1.begin(),flag1.end());
flagz[i] = flag1;
}
for(int i=0; i> (32-(n))))//右移时高位补零
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
//strBaye的长度
unsigned int strlength;
//A,B,C,D的临时变量
unsigned int atemp;
unsigned int btemp;
unsigned int ctemp;
unsigned int dtemp;
//常量ti
const unsigned int k[]={
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
//向左位移数
const unsigned int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
15,21,6,10,15,21,6,10,15,21,6,10,15,21};
const char str16[]="0123456789abcdef";
void mainLoop(unsigned int M[])
{
unsigned int f,g;
unsigned int a=atemp;
unsigned int b=btemp;
unsigned int c=ctemp;
unsigned int d=dtemp;
for (unsigned int i = 0; i < 64; i++)
{
if(i<16){
f=F(b,c,d);
g=i;
}else if (i<32)
{
f=G(b,c,d);
g=(5*i+1)%16;
}else if(i<48){
f=H(b,c,d);
g=(3*i+5)%16;
}else{
f=I(b,c,d);
g=(7*i)%16;
}
unsigned int tmp=d;
d=c;
c=b;
b=b+shift((a+f+k[i]+M[g]),s[i]);
a=tmp;
}
atemp=a+atemp;
btemp=b+btemp;
ctemp=c+ctemp;
dtemp=d+dtemp;
}
//填充函数
unsigned int* add(string str)
{
unsigned int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
unsigned int *strByte=new unsigned int[num*16];
strlength=num*16;
for (unsigned int i = 0; i < num*16; i++)
strByte[i]=0;
for (unsigned int i=0; i >2]|=(str[i])<<((i%4)*8);
}
strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);
strByte[num*16-2]=str.length()*8;
return strByte;
}
string changeHex(int a)
{
int b;
string str1;
string str="";
for(int i=0;i<4;i++)
{
str1="";
b=((a>>i*8)%(1<<8))&0xff; //逆序处理每个字节
for (int j = 0; j < 2; j++)
{
str1.insert(0,1,str16[b%16]);
b=b/16;
}
str+=str1;
}
return str;
}
string getMD5(string source)
{
atemp=A; //初始化
btemp=B;
ctemp=C;
dtemp=D;
unsigned int *strByte=add(source);
for(unsigned int i=0;i