华北电力大学-网络信息安全综合实验报告

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

综合实验报告
( 2013 -- 2014 年度第 1 学期)
名称:网络信息安全综合实验题目:RSA公钥加密解密院系:计算机系
班级:网络工程
学号:
学生姓名:
指导教师:李天
设计周数: 1 周
成绩:
日期:2013年1月18日
一、综合实验的目的与要求
要求:了解RSA产生公钥和私钥的方法,掌握RSA 的加密、解密过程,编写程序设计RSA 加解密工具。

RSA加解密参考:RSA的安全性依赖于大数分解,公钥和私钥都是两个大素数(大于100个十进制位)的函数。

据猜测,从一个密钥和密文推断出明文的难度等同于分解两个大素数的积。

密钥的产生:
1. 选择两个保密的大素数p和q;
2. 计算n=p*q和欧拉函数值E(n)=(p-1)(q-1);
3. 选一整数e,且满足1<e<E(n)和gcd(E(n),e)=1;
4. 计算d,且满足d*e=1 mod E(n);
5. 公钥为{e, n},私钥{d, n}。

二、RSA算法的描述
1.RSA 依赖大数运算,目前主流RSA 算法都建立在1024位的大数运算之上。

而大多数的编译器只能支持到64位的整数运算,即我们在运算中所使用的整数必须小于等于64位,即:0xffffffffffffffff,也就是18446744073709551615,这远远达不到RSA 的需要,于是需要专门建立大数运算库来解决这一问题。

最简单的办法是将大数当作数组进行处理,也就是将大数用0—9这十个数字组成的数组进行表示,然后模拟人们手工进行―竖式计算‖的过程编写其加减乘除函数。

但是这样做效率很低,因为二进制为1024位的大数其十进制也有三百多位,对于任何一种运算,都需要在两个有数百个元素的数组空间上做多重循环,还需要许多额外的空间存放计算的进退位标志及中间结果。

另外,对于某些特殊的运算而言,采用二进制会使计算过程大大简化,这种大数表示方法转化成二进制显然非常麻烦,所以在某些实例中则干脆采用了二进制数组的方法来记录大数,这样效率就更低了。

一个有效的改进方法是将大数表示为一个n 进制数组,n 可以取值为2 的16次方,即0x1000,假如将一个二进制为1024位的大数转化成0x1000进制,它就变成了64位,而每一位的取值范围就不是二进制的0—1或十进制的0—9,而是0-0xffff,我们正好可以用一个无符号整数来表示这一数值。

所以1024位的大数就是一个有64个元素的unsigned int数组,针对unsigned int 数组进行各种运算所需的循环规模至多64次而已。

而且0x10000 进制与二进制,对于计算机来说,几乎是一回事,转换非常容易。

加法: A=Sum[i=0 to p](A[i]*0x10000**i) ;B=Sum[i=0 to q](B[i]*0x10000**i),p>=q ;C=Sum[i=0 to n](C[i]*0x10000**i)=A+B。

如果用carry[i]记录每次的进位则有:C[i]=A[i]+B[i]+carry[i-1]-carry[i]*0x10000,其中carry[-1]=0。

若A[i]+B[i]+carry[i-1]>0xffffffff,则carry[i]=1;反之则carry[i]=0,若carry[p]=0,则n=p;反之则n=p+1。

减法与加法同理。

因此: C[i]=Sum[j=0 to q](A[i-j]*B[j])+carry[i-1]-carry[i]*0x10000,其中carry[-1]=0,
carry[i]=(Sum[j=0 to q](A[i-j]*B[j])+carry[i-1])/0x10000,n=p+q-1,若carry[n]>0,则n=n+1,C[n]=carry
除法设A=Sum[i=0 to p](A[i]*0x10000**i) ,B=Sum[i=0 to q](B[i]*0x10000**i),p>=q, C=Sum[i=0 to n](C[i]*0x10000**i)=A/B。

由于无法将B 对A ―试商‖,我们只能转换成B[q]对A[p]的试商来得到一个近似值,所以我们不能够直接计算C。

但是,我们可以一步一步地逼近C。

显然,(A[p]/B[q]-1)*0x10000**(p-q)<C,令X=0,重复A=A-X*B,X=X+(A[p]/B[q]-1)*0x10000**(p-q),直到A<B。

则有 X=C。

1、选取长度相等的两个大素数p和q,计算其乘积:n = pq
然后随机选取加密密钥e,使e和(p–1)(q–1)互素。

最后用欧几里德扩展算法计算解密密钥d,以满足
ed = 1(mod(p–1) ( q–1))
即d = e–1 mod((p–1)(q–1))
e和n是公钥,d是私钥
2、加密公式如下:ci = mi^e(mod n)
3、解密时,取每一密文分组ci并计算:
mi = ci^d(mod n)
Ci^d =(mi^e)^d = mi^(ed) = mi^[k(p–1)(q–1)+1 ]
= mi mi^[k(p–1)(q–1)] = mi *1 = mi
4、消息也可以用d加密用e解密
三、编程思路
编程思路总共分为以下部分:
1、检验两个数是否互素;
2、由欧拉公式算出相应的密钥d;
3、实现幂函数的取余,从而得以进行解密或加密。

4、实现主要函数编写。

实现RSA算法。

并加解密。

说明:为了方便实现,分组可以小一点,比如两个字母一组。

(1).选择两个大的素数p和q(典型情况下为1024位)
(2).计算n = p * q 和z =(p-1)*(q-1).
(3).选择一个与z互素的数,将它称为d
(4).找到e,使其满足e*d = 1 mod z
提前计算出这些参数以后,我们就可以开始执行加密了。

首先将明文分成块,使得每个明文消息P落在间隔0*P<n中。

为了做到这一点,只要将明文划分成k位的块即可,这里k是满足2^k<n的最大整数。

为了加密一个消息P,只要计算C=P^e(mod n) 即可。

为了解密C,只要计算P=C^d(mod n)即可。

可以证明,对于指定范围内的所有P,加密盒解密互为反函数。

为了执行加密,你需要e和n;为了执行解密,你需要d和n。

因此,公钥是有(e,n)对组成,而私钥是有(d,n)对组成。

实例:根据已知参数:p=3,q=11,M=2,计算公私钥,并对明文进行加密,然后对密文进行解密。

由题意知:n = p * q=33,z =(p-1)*(q-1)=20,选d=7,
计算得e=3,所以
C=M^e(mod n)=8
M=C^d(mod n)=2
四、主要程序说明
1.关键环节
1.求模逆元的扩展欧几里德算法
原理:正整数a和b满足sn*a + tn*b =(a,b),当(a,b)=1 时,sn = a^-1 mod b
其中 s0 = 1,s1 = 0,sj = (sj-2) –(qj-1)*s(j-1;t0 = 0,tj = (tj-2) –(qj-1)*t(j-1) 输入大数 a和b
输出:a^-1 mod b 或0(不存在逆元)
(1)s0 = 1,s1 = 0
(2)while b>0 do
1.1q = a/b;s2 = s0-q*s1;
1.2s0 = s1;s1 = s2;
1.3t = b;b = a%b;a = t;
(3)if a=1 return s0;else return 0;
2.明文(数值)长度要小于key_N,才符合RSA算法,否则很容易出错,我选择了一个大于key_N的明文,发现经过验证并不符合RSA算法。

那么,处理那些明文(数值)很大的办法就是将明文分成一小段一小段,否则,解密的信息必然是一堆乱码(除非原明文数值并不大)。

分成一小段之后,然后利用加密公式和解密公式对其进行处理,这样得到的结果就是准确结果了。

2.主要函数
公钥和私钥的生成void generate_key()
(1)首先利用strongprime函数生成两个素数key_P_Q[0],key_P_Q[1],然后利用multiply函数得到key_N = key_P_Q[0] * key_P_Q[1],用subtract函数和add函数实现key_Z =
(key_P_Q[0] - 1 ) * (key_P_Q[1] – 1) = key_N –
(key_P_Q[0] + key_P_Q[1]) + 1。

(2)随机生成一个密钥key_D,然后利用extend_gcd函数求同余方程key_D *key_E mod key_Z = 1。

得到key_E,作为公钥。

(3)至此将公钥(key_N,key_E)写入文件中,将私钥(key_N,key_D)写入文件中保存。

加密信息void encode_information()
将加密的信息长度判断是否大于key_N的长度,如果大于key_N的长度,应该将加密信息分成一小段一小段,各小段长度均小于key_N长度,然后利用读取已经保存在文件中的公钥和私钥,对加密信息每小段每小段进行加密,并将加密信息密文存放到加密文件中。

加密将用到的函数为powmod(key_P,key_E,key_N,
key_C)。

解密信息void decode_information()
解密信息和加密信息采用同样的道理,即利用保存好的密钥对密文一段一段进行解密。

解密将用到的函数为Powmod(key_C,key_D,key_N,key_P)
五、实验结果
(1)公钥长度为1024位进行验证:
图(1)生成公钥和私钥
图(2)加密文件e:\20042432\1.txt,并显示加密文件中信息
图(3)解密文件,并显示解密文件中信息
图(4)选择键。

可以选择4程序结束
图(5)e:\20042432\1.txt加密后的密文
(2)公钥长度为2048位进行验证:
图(6)生成公钥和私钥
图(7)对e:\20042432\2.txt文本文件进行加密
图(8)对加密密文e:\20042432\key_C.dat进行解密,并存放在e:\222.txt中,并在屏幕上显示解密后的信息。

图(9)图中可以看到其中原明文信息为2.txt,对它加密后key_P_Q.dat中存放两个大素数,key_N.dat存放两个素数相乘后的结果。

Key_D为随机生成的与key_Z(存放在key_Z.dat文件中)(即key_Z = key_N – key_P_Q[0] – key_P_Q[1] + 1)互素的大整数,
存放在key_D.dat文件中。

key_E为解同余方程后的大数,存放在key_E.dat文件中。

其中key_C为加密后的明文信息,存放在key_C.dat文件中,并在解密时对这个密文文件进行解密。

图(10)e:\20042432\2.txt加密后的密文
六、综合实验总结
通过这次试验我对非对称密码算法RSA的理解加深了,会运用一些现成的算法进行编程,对一些比较复杂的算法开始基本认识并深刻的掌握。

对对称密码算法DES更加的深刻,程序要仔细耐心调试察看,不要被错误吓到,同时在修改老师给的源程序的时候懂得了如何细心的认真的仔细的看每一个编程程序。

通过这次的实验,了解了非对称密码算法RSA,会运用一些现成的算法进行编程,对一些比较复杂的算法开始基本认识并深刻的掌握。

在以后所涉及这方面的知识将会有全新的了解和掌握。

RSA的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,因为没有证明破解RSA就一定需要作大数分解。

经过这次设计,使我学到了很多很多。

首先为对大数的认识。

大数的存储形式灵活多样,可以1,0二进制存储,可以十进制存储,
也可以2**n进制存储。

对于讲究空间效率来讲,显然,寻找合适的n,使用2**n进制存储数据将会高效的多。

而且,对于大数的运算技巧也要要求思考的严密一些。

我通过参考miracl大数库,通过阅读相关代码,弄懂了其大数存储及实现机制。

而且,现在想来,我觉得我在阅读代码所花费的时间上有所不值。

我想,如果我将花费的大半时间用来自己写一个大数库,将会实在的多。

已经为我们设计好的大数库虽然用起来方便,但阅读起来,并弄懂其思想并不简单。

特别是我认识到老外写的代码灵活性太强,他们写代码没有规矩的,想到哪写到哪,非常难以阅读,特别在实现RSA算法中加密解密的算法时,我始终就没有看懂到底在写些什么,后来我干脆自己来写,一写,发现错误百出。

但经过我一遍又一遍的调试,终于能够顺利实现了课程设计所要求的功能,而且,我自己写的代码结构性非常强,很好阅读。

算法实现也简单易懂。

然而,唯一遗憾之处就是借用了大数库里现成的一些函数,我觉得这是我比较惭愧的,如果我自己来写个大数库,也许心里会心安理得一些。

不过,我完全弄懂了miracl大数库里相关函数的运算机制。

并掌握了其实现机制。

可以说,通过阅读别人的代码,也学到了不少。

总的来说,我这次课程设计时间分配不大合理,花在阅读代码的时间太长,事实上它占据了我大部分时间,如果我用这个时间自己来写大数运算库,恐怕早写完了。

活儿非常苦。

不过我依然欣慰,我从别人哪里的确学到了不少。

程序运行相对顺利,通过读取代码我们了解到密钥生成中和加密处理一般都是依赖于数据的位运算。

位运算,对于计算机而言是最简单的运算。

对于加密这种需要频繁执行的程序应用安慰运算应是最为合适的。

可以修改成为大数分解算法。

目前,RSA 的一些变种算法已被证明等价于大数分解。

不管怎样,分解n是最显然的攻击方法。

现在,人们已能分解多个十进制位的大素数。

因此,模数n 必须选大一些,因具体适用情况而定。

四、参考文献
[1] 斯托林斯. 网络安全基础应用与标准. 清华上大学出版社, 2007.7
[2] 马民,赵虹,孙犁等. 计算机技术在电力系统故障诊断中的应用. 计算机学报,
2003,17(4):13~23
[3]高传善等主编《数据通信与计算机网络》(第2版)高等教育出版社 1999年
[4]陈建伟等主编《计算机网络与信息安全》中国林业出版社 2006年
程序流程图
核心代码:
extern "C"
{
#include "miracl.h"
}
#include "big.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include"malloc.h"
static miracl *mip;
static big pd,pl,ph;
#define PRIME_BITS 512
//===============================================================
int abs(int x)
{
if (x>= 0)
return x;
else return (-x);
}
//==================================================================
// 产生一个强素数
//==================================================================
void strongprime(big p,int n,long seed1,long seed2)
{
int r,r1,r2;
irand(seed1); //产生一个随机数,即初始化
bigbits(2*n/3,pd); //生一个2*n/3 位(bit)的pd随机数
nxprime(pd,pd); //nxprime(pd,x)找到一个x大于pd的素数,返回值为BOOL expb2(n-1,ph); //ph = 2^(n-1),即2的(n-1)次方
divide(ph,pd,ph); //ph = ph/pd
expb2(n-2,pl);
divide(pl,pd,pl); //pl=pl/pd
subtract(ph,pl,ph); //ph = ph-pl
irand(seed2);
bigrand(ph,ph);
add(ph,pl,ph);
r1=subdiv(pd,12,pl); //pl=pd/12
r2=subdiv(ph,12,pl); //pl=ph/12
r=0;
while ((r1*(r2+r))%12!=5)
r++;
incr(ph,r,ph); //ph=ph+r
do
{ //find p=2*r*pd+1 = 11 mod 12
multiply(ph,pd,p); //p = ph*pd
premult(p,2,p); //p = p*2
incr(p,1,p); //p = p+1
incr(ph,12,ph); //ph = ph+12
} while (!isprime(p));
}
//=============================================================== //同余 extend_gcd
//=============================================================== void extend_gcd(big key_D,big key_Z,big key_E){
big s0,s1,s2,zero,z1;
s0 = mirvar(1);//初始化为1
s1 = mirvar(0);
s2 = mirvar(0);
zero = mirvar(0);
z1 = mirvar(0);
copy(key_Z,z1);
big q,t;
q = mirvar(0);
t = mirvar(0);
while(compare(key_Z,zero) > 0)
{
copy(key_Z,t); //t = key_Z
divide(key_D, key_Z,q); //q= key_D / key_Z
copy(key_D, key_Z); // key_Z = key_D
copy(t, key_D); // key_D = t
multiply(q,s1,t); //t = q*s1
subtract(s0,t,s2); //s2 =s0 -q*s1
copy(s1,s0); //s0 = s1
copy(s2,s1); //s1 =s2
}
convert(1,t);//t = 1
if(compare(t, key_D) != 0)
convert(0, key_E);
else
{
if(compare(s0,zero) > 0)
copy(s0, key_E);
else
add(s0,z1, key_E);
}
return;
}
//===============================================================
// 生成公钥和私钥 generate_key()
//===============================================================
void generate_key()
{
int i;
long seed[4];
big one,key_P_Q[2],key_N,key_Z,key_D,key_E/*,key_ZR*/;
FILE *outfile;
mip=mirsys(100,0);
//初始化操作
pd=mirvar(0); pl=mirvar(0); ph=mirvar(0);
one = mirvar(0);
key_P_Q[0]=mirvar(0);
key_P_Q[1]=mirvar(0);
key_N = mirvar(0);
key_Z = mirvar(0);
key_D = mirvar(0);
key_E = mirvar(0);
for (i=0;i<4;i++)
seed[i] = abs(brand());
printf("\t\t\n正在产生公钥和私钥,请等候……\n");
//产生两个素数
strongprime(key_P_Q[0],PRIME_BITS,seed[0],seed[1]);
strongprime(key_P_Q[1],PRIME_BITS,seed[2],seed[3]);
multiply(key_P_Q[0],key_P_Q[1],key_N);//key_N = key_P_Q[0] * key_P_Q[1] mip->IOBASE=16;
//写到key_P_Q.dat文件中
outfile=fopen("key_P_Q.dat","wt");
cotnum(key_P_Q[0],outfile);
cotnum(key_P_Q[1],outfile);
fclose(outfile);
printf("\t\t\n公钥长度key_N = key_P_Q[0] * key_P_Q[1] 有%d 位!\n",logb2(key_N));
printf("\n\t===========输出公钥 key_N================\n");
cotnum(key_N,stdout);
mip->IOBASE=16;
//写到key_N.dat文件中
outfile=fopen("key_N.dat","wt");
cotnum(key_N,outfile);
fclose(outfile);
//key_Z = key_N - key_P_Q[0] - key_P_Q[1] + 1;
convert(1,one);
subtract(key_N,key_P_Q[0],key_Z);
subtract(key_Z, key_P_Q[1],key_Z);
add(key_Z,one,key_Z);
mip->IOBASE=16;
//写到key_Z.dat文件中
outfile=fopen("key_Z.dat","w+");
cotnum(key_Z,outfile);
fclose(outfile);
printf("\n\t===========输出解密密钥 key_D===============\n");
do
{
bigrand(key_P_Q[0],key_D);
subtract(key_D,one,key_D);
}while(!isprime(key_D));
cotnum(key_D,stdout);
mip->IOBASE=16;
//写到key_D.dat文件中
outfile=fopen("key_D.dat","w+");
cotnum(key_D,outfile);
fclose(outfile);
printf("\n\t==========-输出加密密钥 key_E===============\n");
extend_gcd(key_D,key_Z,key_E);
cotnum(key_E,stdout);
mip->IOBASE=16;
//写到key_E.dat文件中
outfile=fopen("key_E.dat","w+");
cotnum(key_E,outfile);
fclose(outfile);
}
//=============================================================== // 加密 encode_information()
//=============================================================== void encode_information()
{
big key_N,key_E,key_P,key_C;
char buffer[130],ifname[32];
int buffer_length,i = 0;
FILE *infile,*outfile;
BOOL flag;
key_N = mirvar(0);
key_E = mirvar(0);
key_P = mirvar(0);
key_C = mirvar(0);
if ((infile=fopen("key_N.dat","rt"))==NULL)
{
printf("\n不能打开文件key_N.dat");
return ;
}
mip->IOBASE=16;
cinnum(key_N,infile);
fclose(infile);
if ((infile=fopen("key_E.dat","rt"))==NULL)
{
printf("\n不能打开文件key_E.dat");
return ;
}
mip->IOBASE=16;
cinnum(key_E,infile);
fclose(infile);
printf("\t要加密的文件为 = ");
getchar();
gets(ifname);
if ((infile=fopen(ifname,"rt"))==NULL)
{
printf("\n不能打开文件 %s\n",ifname);
return ;
}
else
{
printf("\n正在加密信息……\n");
outfile =fopen("key_C.dat","w+");
if (fgets(buffer,128,infile) == NULL)
flag = true;
else
flag = false;
while(!flag)
{
buffer_length = strlen(buffer);
buffer[buffer_length] = '\0';
mip->IOBASE=128;
cinstr(key_P,buffer);
cotnum(key_P,stdout);
powmod(key_P,key_E,key_N,key_C);
mip->IOBASE=16;
cotnum(key_C,outfile);
if (fgets(buffer,128,infile) == NULL)
flag = true;
}
printf("\n");
fclose(infile);
fclose(outfile);
}
}
//=============================================================== //解密 decode_information()
//=============================================================== void decode_information()
{
big key_N,key_D,key_C,key_P;
char ifname[32];
FILE *infile,*outfile;
key_N = mirvar(0);
key_D = mirvar(0);
key_C = mirvar(0);
key_P = mirvar(0);
//打开key_N.dat文件,写入key_N
if((outfile = fopen("key_N.dat","rt")) == NULL)
{
printf("不能打开key_N.dat文件\n");
return ;
}
mip->IOBASE=16;
cinnum(key_N,outfile);
fclose(outfile);
//打开key_D.dat文件,写入key_D
if((outfile = fopen("key_D.dat","rt")) == NULL)
{
printf("不能打开key_D.dat文件\n");
return ;
}
mip->IOBASE=16;
cinnum(key_D,outfile);
fclose(outfile);
printf("\t要解密出的文件存储在 = ");
getchar();
gets(ifname);
infile=fopen(ifname,"wt");
printf("\t============解密出的明文信息===============\n");
//打开key_C.dat文件,一段一段写入key_C
if((outfile = fopen("key_C.dat","rt")) == NULL)
{
printf("不能打开key_C.dat文件\n");
return ;
}
while(1)
{
mip->IOBASE=16;
cinnum(key_C,outfile);
if (size(key_C)==0) break;
powmod(key_C,key_D,key_N,key_P);
mip->IOBASE=128;
cotnum(key_P,infile);
cotnum(key_P,stdout);
}
printf("\t===========解密信息结束====================\n");
fclose(outfile);
fclose(infile);
}
//=============================================================== // 主程序
//=============================================================== int main()
{
char ch;
do{
system("cls");
printf("\t\t==============请选择菜单!==============\n");
printf("\t\t* 1:生成公钥和私钥 *\n");
printf("\t\t* 2:加密信息 *\n");
printf("\t\t* 3:解密信息 *\n");
printf("\t\t*=============4:退出===================\n");
printf("\n\t\t请输入你要选择的菜单项=");
ch = getchar();
if(ch == '1')
{
generate_key();
system("pause");
}
else if(ch == '2')
{
encode_information();
system("pause");
}
else if(ch == '3')
{
decode_information();
system("pause");
}
}while(ch != '4');
return 0;
}
//===============================================================。

相关文档
最新文档