了保证数字签名的真实性。
数字签名的原理是:(发送方和接收方根据要求各自产生自己的一对公钥和私钥)
1)被发送文件采用某种算法对原始消息进行运算,得到一个固定长度的数字串,称为消息摘要(MD),不同的消息得到的消息摘要各异,但是对相同的消息它的消息摘要却是唯一的;
2)发送方生成消息的消息摘要,用自己的私钥对摘要进行加密来形成发送方的数字签名;
3)这个数字签名将作为消息的附件和消息一同用接收方的公钥进行加密,将加密后的密文一起发送给接收方;
4)接收方首先把接收到的密文用自己的私钥解密,得到原始消息和数字签名,再用发送方的公钥解密数字签名,随后用同样的算法计算出消息摘要;
5)如果计算出来的消息摘要和发送方发送给他的消息摘要(通过解密数字签名得到的)是相同的,这样接收方就能确认数字签名确实是发送方的,否则就认为收到的消息是伪造的或是中途被篡改的。
数字签名的原理图如2-1所示
A B
D E D E 用A的私钥加密 用B的公钥 用B的私钥 用A的公钥解密 数字签名 加密 解密 核实签名
图2-1 数字签名的原理
2.2.2 RSA数字签名算法的实现原理 RSA数字签名算法分为以下两个步骤:
1)签名算法(包括两步:消息摘要计算,RSA加密)
(1)消息摘要MD的计算:消息在签名前首先通过MD5计算,生成128位的消息摘要;MD5函数是一种单向散列函数,它将任意长度的消息压缩成128位的消息摘要。应用MD5的单向性(即给定散列值,计算消息很难)和抗碰撞性(即给定消息M,要找到另一消息M’并满足两者的散列值很难),可以实现信息的完整性检验。另外该函数的设计不基于任何假设和密码体制而直接构造,执行的速度快,是一种被广泛认可的单向散列算法。
(2)对MD作RSA加密算法:采用签名者的私钥加密消息摘要,得到加密后的字符串即数字签名;
2)验证签名算法(RSA解密、对消息摘要计算和比较)
第 5 页
验证签名算法包括两步:RSA解密得签名者的消息摘要,验证者对原消息计算摘要,比较两个消息摘要。验证签名的过程输入为消息,签名者的公钥,签名;输出为验证的结果,即是否是正确的签名。
(1)RSA解密:签名实际是加密的消息摘要,用以上所述的RSA解密方法采用签名者的公钥对这个加密的消息摘要解密,解密的结果应为128位的消息摘要。
(2)消息摘要计算和比较:验证者对消息用MD5算法重新计算,得到验证者自己的消息摘要。验证者比较解密得到的消息摘要和自己的消息摘要,如果两者相同,则验证成功,可以确认消息的完整性及签名确实为签名者的;否则,验证失败,确认签名被冒充或是被篡改。
3 RSA数字签名的逻辑设计与实现
3.1 RSA数字签名的总体设计
3.1.1 RSA数字签名所需实现的功能 在本软件中需要实现的功能有以下几个:
(1)生成RSA密钥:公钥ke=(e,n),私钥kd=(d,n); (2)利用MD5算法计算出消息摘要MD;
(3)数字签名的实现:用私钥d对消息摘要进行加密计算(RSA算法中的加密方法);
(4)验证数字签名:用公钥e对数字签名进行解密计算(RSA算法中的解密方法),得到的解密结果与(2)步计算出的消息摘要比较,如果两个消息摘要一样则签名成功。
3.1.2 总体要求和设计 本软件的总体要求有:
1)按要求生成非对称密钥——公钥和私钥;
2)按任意写入的的消息字符串(明文信息)生成所需要的消息摘要MD; 3)在本设计中用产生的私钥d根据RSA算法的加密原理对所生成的消息摘要进行加密运算,得到数字签名;
4)在本设计中用产生的公钥e根据RSA算法的解密原理对所加密的消息摘要即数字签名进行解密运算,得到对应的消息摘要(在本设计中标示的为解密信息),比较两个消息摘要,验证数字签名者的身份的真实与否;
5)提示信息完整、操作舒适、图形界面雅观。
本软件的总体设计都是基于C++的开发环境,采用的是Microsoft Visual c++ 6.0的运行环境。
第 6 页
3.2 各部分的设计实现
3.2.1 密钥产生的实现
在密钥的产生部分中起决定性作用的是素数的选择, 对随机数作素性检测,若通过则为素数;否则增加一个步长后再做素性检测,直到找出素数。素性检测采用Fermat测试。这个算法的理论依据是费尔马小定理:如果m是一个素数,且a不是m的倍数,那么根据费尔马小定理有:a m-1=1 ( mod m)。实际应用时:a m-1 = 1
( mod m)? a m = a ( mod m) ?a= a m ( mod m),因此对于整数m,只需计算a m ( mod m),
再将结果与a比较,如果两者相同,则m为素数。选取a=2,则a一定不会是任何素数的倍数。根据所选的素数的不同产生不同的密钥。
密钥的理论产生模块流程图如图3-1所示:
产生任意素数p和q 计算n=p*q 计算ou_la=(p-1)(q-1) 选择e作为公钥 计算d作为私钥
图3-1 密钥产生
密钥产生的部分代码实现:
CString str;
//第一步 产生任意素数 GeneratePrimeNumbers(); //第二步 计算 n=p*q m_n = m_Prime1 * m_Prime2; //第三步 0=(p-1)(q-1)
m_Undef = (m_Prime1-1) * (m_Prime2-1); //第四步选择'e' SelectE();
第 7 页
//第五步计算D CalculateD(); //显示公钥和私钥 //(1) 公钥 KU={e,n}
str.Format(\m_public_key.SetWindowText(str); //(2) 私钥KU={d,n}
str.Format(\m_private_key.SetWindowText(str); // 选择一个 'e' 使 'e'与 m_Undef互素
//选择e的函数的实现 SelectE() {CString str;
for(float i=2;i<100000;i++) }
//互为素数的函数的实现
IsRelativePrime(float X,float Y) {float R; //输入: X,Y
if(X { } //在这时X总是比Y大 for(;;) X=X+Y; Y=X-Y; X=X-Y; { } if(IsRelativePrime((float)m_Undef,(float)i)) { } m_e=(long)i; return; 第 8 页 } { } return false; return true;//互素 if(Y==0) break; R=fmod(X,Y); X=Y; Y=R; if(X != 1) else // 计算D的函数的实现 CalculateD() { } //产生素数的函数的实现 GeneratePrimeNumbers() { CString str; UpdateData(TRUE); float d; long d_dash; CString str; for(float k=1;k<100000;k++) { } d=(m_Undef*k+1)/m_e; d_dash = (long)((m_Undef*k+1)/m_e); if(d == d_dash) { } m_d=d; return; 第 9 页