数字签名算法RSAWord格式.docx
《数字签名算法RSAWord格式.docx》由会员分享,可在线阅读,更多相关《数字签名算法RSAWord格式.docx(20页珍藏版)》请在冰豆网上搜索。
如果一个消息的签名是从别处复制得到的,则任何人都可以发现消息与签名之间的不一致性,从而可以拒绝签名的消息;
4)签名的消息是不可改变的:
经签名的消息不能篡改,一旦签名的消息被篡改,任何人都可以发现消息与签名之间的不一致性;
5)签名是不可抵赖的:
签名者事后不能否认自己的签名。
可以由第三方或仲裁方来确认双方的信息,以做出仲裁。
2、各模块功能描述
模块一创建创建密钥容器,得到CSP句柄
微软的CryptoAPI是PKI推荐使用的加密API。
其功能是为应用程序开发者提供在Win32环境下使用加密、验证等安全服务时的标准加密接口。
CryptoAPI处于应用程序和CSP之间(见图一)。
图一
从图一可以看到,每个CSP有一个密钥库,密钥库用于存储密钥。
而每个密钥库包括一个或多个密钥容器(KeyContainers)。
每个密钥容器中含属于一个特定用户的所有密钥对。
每个密钥容器被赋予一个唯一的名字。
在销毁密钥容器前CSP将永久保存每一个密钥容器,包括保存每个密钥容器中的公/私钥对。
在这个模块中,实现创建密钥容器,得到CSP密钥句柄的作用,并且它被绑定到以UserName为名的密钥容器上。
模块二计算Hash值,签名
在这个模块中,实现实现对原始报文的签名。
首先,对原始报文进行散列值的计算。
此处通过CryptoAPI中的函数,直接调用实现。
然后调用CryptSignHash(),对散列值进行签名。
过程如图二所示。
图二
模块三验证签名
负责验证签名的人在收到签名者发来的公钥、数据及签名后,先用CryptImportKey()将签名者的公钥导入密钥容器中,验证者通过对原文的Hash计算,并与收到的Hash值对比,验证是否是发送方发送的消息,并可验证其正确性。
过程如图三所示。
图三
三、概要设计
1、函数CryptAcquireContext(),获得指定CSP容器的句柄。
主要参数表如下:
PhProv()CSP句柄指针
PszContainer()密钥容器名称,指向密钥容器的字符串指针
Pszprovider()指向CSP名称的字符串指针
这个函数用来实现取得指定CSP句柄密钥容器,以后任何的加密都是针对这个CSP句柄而言。
2、函数CryptGenKey(),用来随即产生密钥。
主要参数如下:
hCryptProv,CSP句柄
AT_SIGNATURE,创建的密钥对类型为signaturekeypair
0,key类型,这里用默认值
&
hKey创建成功返回新创建的密钥对的句柄
3、函数CryptCreateHash(),用来创建Hash对象。
参数如下:
HCRYPTPROVhProv,CSP句柄
ALG_IDAlgid,选择hash算法,比如CALG_MD5等
HCRYPTKEYhKey,HMAC和MAC算法时有用
DWORDdwFlags,保留,传入0即可
HCRYPTHASH*phHash返回hash句柄
4、函数CryptHashData(),用来Hash数据。
HCRYPTHASHhHash,hash对象
BYTE*pbData,被hash的数据
DWORDdwDataLen,数据的长度
DWORDdwFlags微软的CSP这个值会被忽略
5函数CryptDeriveKey(),用于
调用CryptDeriveKey获取对话密码,参数如下:
hCryptProv,
CSP句柄
CALG_RC2,
一个ALG_ID结构,用来指定对称密钥生成的算法
hHash,
哈希对象
CRYPT_EXPORTABLE,
指定生成密钥的类型,CRYPT_EXPORTABLE意味着这个程序生成的密钥可以被其它程序调用,而不是仅仅限于这个程序当中。
但是它不能用于非对称密码中。
hKey指向生成的密钥
5、总体设计
发送方验证方
四、详细设计
1、RSA算法描述
RSA算法是一种公钥密码算法,实现RSA算法包括生成RSA密钥,加密和解密数据。
RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。
RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一。
RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价。
即RSA的重大缺陷是无法从理论上把握它的保密性能如何,而且密码学界多数人士倾向于因子分解不是NP-C问题。
RSA的缺点主要有:
A)产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密。
B)分组长度太大,为保证安全性,n至少也要600bits。
RSA算法的实现原理:
1)随机选择两个不同的素数p和q,它们的宽度是密钥宽度的二分之一。
2)计算出p和q的乘积n。
3)在2和Φ(n)之间随机选择一个数e,e必须和Φ(n)互素,整数e用做加密密钥(其中Φ(n)=(p-1)*(q-1))。
4)从公式ed≡1modΦ(n)中求出解密密钥d。
5)得公钥(e,n),私钥(d,n)。
6)公开公钥,但不公开私钥。
7)将明文P(假设P是一个小于n的整数)加密为密文C,计算方法为:
C=P^emodn;
8)将密文C解密为明文P,计算方法为:
P=C^dmodn;
然而只根据n和e(不是p和q)要计算出d是不可能的。
因此,任何人都可对明文进行加密,但只有授权用户(知道d)才可对密文解密。
2、创建密钥容器,得到CSP句柄
此处调用函数CryptAcquireContext(),创建密钥容器,当Flagvalues值为零,说明以UserName为名的密钥容器存在,那么我们已经得到了CSP的句柄
具体实现代码如下:
if(CryptAcquireContext(
hCryptProv,//返回CSP句柄
UserName,//密码容器名
NULL,//NULL时使用默认CSP名(微软RSABaseProvider)
PROV_RSA_FULL,//CSP类型
0))//Flagvalues
{
printf("
得到了CSP句柄"
UserName);
}
如果密钥容器不存在,我们需要重新创建这个密钥容器:
&
hCryptProv,
UserName,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))//创建以UserName为名的密钥容器
{
//创建密钥容器成功,并得到CSP句柄
一个新的密钥容器被创建\n"
);
else
HandleError("
创建失败.\n"
}
此时就已经创建了密钥容器,并得到了CSP的句柄。
也可以这样理解,我们得到了一个CSP的句柄,并且它被绑定到以UserName为名的密钥容器上。
可以如下删除密钥容器。
CryptAcquireContext(&
hCryptProv,userName,NULL,PROV_RSA_FULL,CRYPT_DELETEKEYSET);
3、RSA算法描述
具体程序实现如下:
1、读取原文
voidCGenRsaKey:
:
OnSOpenButton()
//TODO:
Addyourcontrolnotificationhandlercodehere
UpdateData(TRUE);
charszFilter[]="
全部文件(*.*)|*.*||"
;
CFileDialogOpenFile(TRUE,NULL,"
*.*"
OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR,szFilter,NULL);
if(OpenFile.DoModal()!
=IDOK)
return;
m_strSSrcPath=OpenFile.GetPathName();
UpdateData(FALSE);
return;
}
//产生随机密钥
OnVerifySaveButton()
staticcharszFilter[]="
CFileDialogsaveFileDlg(FALSE,NULL,"
OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR,szFilter,NULL);
if(saveFileDlg.DoModal()!
m_strVDestPath=saveFileDlg.GetPathName();
OnRsaSign()
if(m_strSSrcPath=="
"
m_strVDestPath=="
)
MessageBox("
请选择待签名/待验证文件路径!
//读取签名原文失败
CFilefpSrcFile;
if(fpSrcFile.Open(m_strVDestPath,CFile:
modeRead)==0)
读取签名原文失败!
nSrcLen=fpSrcFile.GetLength();
pbSrcData=newunsignedchar[nSrcLen+1];
memset(pbSrcData,0x00,nSrcLen+1);
fpSrcFile.Read(pbSrcData,nSrcLen);
fpSrcFile.Close();
2、获得CSP句柄
intCKeyOperation:
CRYPTAPI_RSAVerify(unsignedchar*pbSrcData,intnSrcLen,
unsignedchar*pbDestData,intnDestLen)
if(!
m_hProv,"
ASYSIGN"
MS_ENHANCED_PROV,PROV_RSA_FULL,0))
return-1;
CryptGetUserKey(m_hProv,AT_SIGNATURE,&
m_hKey))
if(m_hProv)
CryptReleaseContext(m_hProv,0);
return-2;
}
3、创建Hash对象并Hash数据
HCRYPTHASHhHash;
CryptCreateHash(m_hProv,CALG_SHA,0,0,&
hHash))//创建HASH对象
//释放CSP句柄
return-3;
CryptHashData(hHash,pbSrcData,(unsignedlong)nSrcLen,0))//hash数据
if(hHash)
CryptDestroyHash(hHash);
//释放Hash句柄
4、签名数据
//获得签名数据长度
CryptSignHash(hHash,AT_SIGNATURE,NULL,0,NULL,(unsignedlong*)pnDestLen))
return-4;
//签名HASH数据
CryptSignHash(hHash,AT_SIGNATURE,NULL,0,pbDestData,(unsignedlong*)pnDestLen))
return-5;
if(hHash)
CryptDestroyHash(hHash);
if(m_hProv)
return0;
5、读取签名数据
CFilefpDestFile;
if(fpDestFile.Open(m_strSSrcPath,CFile:
nDestLen=fpDestFile.GetLength();
fpDestFile.Read(pbDestData,nDestLen);
fpDestFile.Close();
CFilefpDestFile;
6、Hash待验证数据
//创建HASH对象
hHash))
//HASH待验证数据
CryptHashData(hHash,pbSrcData,nSrcLen,0))
7、验证签名数据
//验证签名数据
CKeyOperationobj_Verify;
intr=obj_Verify.CRYPTAPI_RSAVerify(pbSrcData,nSrcLen,pbDestData,nDestLen);
if(r!
=0)
验证签名失败!
delete[]pbSrcData;
pbSrcData=NULL;
MessageBox("
验证签名成功!
if(!
CryptVerifySignature(hHash,pbDestData,nDestLen,m_hKey,NULL,0))
if(hHash)
CryptReleaseContext(m_hProv,0);
五、测试数据及其结果分析
测试时,在签名原文中输入数据:
1234567890
生成RSA密钥对,并导出公钥
程序显示产生RSA密钥对成功,说明在签名时产生密钥对运行没有问题。
对签名原文进行签名,程序运行如下:
程序运行显示签名数据成功,签名后数据长度为128字节,签名后的数据存放在.txt文档中,便于在下一步进行验证比较。
签名后的数据显示如下:
记事本显示为乱码,因为输出为16进制,在记事本中的显示就为乱码,当放入控制台时,显示就为16进制数。
发现数据长度为128字节,签名正确。
对签名进行验证,程序运行如下:
待验证文件选择为签名后的数据,签名原文选择原文件。
按下验证签名按钮,产生对话框,说明验证签名成功,程序运行无误。
六、软件设计总结
本次的课程设计,主要是熟悉了数字签名的具体过程,并了解了RSA算法的具体实现步骤,更重要的是学会了CryptoAPI的使用,通过调用其中的函数,很方便的实现了加密以及签名的功能。
再次总结一下收获。
1、关于CSP
CSP是真正执行加密工作的独立的模块。
物理上一个CSP由两部分组成:
一个动态链接库,一个签名文件。
若加密算法用硬件实现,则CSP还包括硬件装置。
其决定了以下因素:
(1)
有且仅有一个密钥交换算法;
(2)
有且仅有一个签名算法;
(3)
特定的Key
Blob格式;
(4)
特定的数字签名格式;
(5)
特定的密钥推导模式;
(6)
特定的密钥长度;
(7)特定的分组加密算法的缺省模式。
2、
几个误区
通过本次数字签名的实验,我通过上网查阅资料等方式,发现存在着这样几个误区。
误区一:
大家对公钥私钥区分得太死板了。
其实当你把公钥保密不公开的时候,公钥就是私钥了;
当你把私钥公开的时候,私钥也是公钥了。
没必要记得这么死的。
密钥与私钥只是相对的概念,RSA中产生的e与d,当把e公布出去时,e就是公钥,d就是私钥。
相反的e就变成了私钥。
误区二:
对RSA算法的原理理解不够,实际上所谓的公钥和私钥只是RSA算法(说穿了RSA就是个数学方程式)的参数(未知数),比如X+Y+M=Z,X就可以说是私钥,Y就可以说是公钥,M就是需要加密的内容,Z就是加密后的密文,当然RSA中不可能只有X和Y两个未知数的,所以就经常有朋友问,到底X是私钥还是Y是
钥。
其实这个就要取决于你用在时候,什么地方了。
误区三:
对Security.Cryptography命名空间不熟悉。
“数字签名”一般的做法是:
A先计算出文件M的HASH码,再对HASH码进行加密(这个步骤就是签名),再把M(文件M不要加密,第三方可以查阅)和加密后的HASH码传送给B,B再用A的公钥来解密刚才得到的加密HASH码,如果能解密,那就说明这个文件是A发的,具有法律效应。
再计算出得到的文件M的HASH码,再和刚才解密出来的HASH码比较(这个步骤叫验证签名),如果一致就说明文件M在传输过程中没有被修改。
但是需要解密RSA,就必须提供公钥和私钥,当然这和我们的现实不符,因为A不可能把他的私钥给B。
许多人就是在这里难住了。
其实Security.Cryptography命名空间中有RSA