去年做的密码学开发实践,其实这东西看穿了也没啥难的,别看题目起的多吓人,其实程序是简单的很,哈哈,今天懒得打字了,直接把当时的课程设计报告部分内容copy过来吧。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
设计背景
随机数在密码学中起着极其重要的作用,例如在相互认证、产生密钥等的过程中都需要使用高强度的随机数来保证安全。所谓的高强度就是指随机数序列需要满足随机性和不可预测性。
真正的随机数实际上是难以获得的,因此通常意义上我们使用的都是伪随机数,但如果算法设计的好,伪随机数也可以通过各种随机性检验。
基于ANSI X9.17的伪随机数产生器是密码强度最高的伪随机数产生器之一,目前已经在包括PGP等许多应用中被采纳。
设计目的
该例是应用密码算法程序设计的课程设计,要求在深入理解ANSI X9.17随机数产生的流程基础上,使用一种编程语言实现一个基于ANSI X9.17的随机数发生器。
要求生成的随机数长度为1024比特,使用十六进制表示时为128字节;并且要求拥有良好的用户界面,使用户可以很快上手,易学易用。
理论知识介绍
ANSI X9.17标准是美国国家标准化协会制订的金融机构密钥管理规范;ANSI(美国国家标准化协会)负责金融安全的小组是ASC X9和ASC X12。其中ASC X9负责制定金融业务标准,ASC X12负责制定商业交易标准。其中ANSI X9.17标准制定于1985年,对金融机构的密钥管理进行了规范。
在多数情况下,我们都是使用某种特定的密码算法来辅助完成随机数的产生,ANSI X9.17也不例外,它的算法就是基于大名鼎鼎的DES(数据加密标准)。
数据加密标准(Data Encryption Standard,DES)是迄今世界上最为广泛使用和流行的一种分组密码算法,它的分组长度为64比特,密钥长度为56比特,它是由美国IBM公司研制的,源于早期的一种被称为Lucifer密码的发展和修改。
DES是一个迭代型的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行“异或”运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用16个循环。
为了提高安全性,通常人们并不使用单纯的DES,而是将DES算法在多密钥情况下多重使用,例如两个密钥的三重DES。此方案已经被证明了是行之有效的,已在密钥管理标准ANSI X9.17和ISO 8732中被采用。
关于DES的详细计算过程就不说了,下图是ANSI X9.17的实现逻辑:
在上图中的EDE表示两个密钥的三重DES算法;产生器主要有3个组成部分。
1) 输入 输入为两个64比特的伪随机数,其中DTi表示当前的日期和时间;每产生一个数Ri后,DTi都会更新一次;Vi是产生第i个随机数时的种子,其初值可任意设定,以后每次都会自动更新。
2) 密钥 产生器使用了3次三重DES加密,3次加密使用相同的两个56比特密钥K1和K2,这两个密钥必须保密且不能用作他用。
3) 输出 输出为一个64比特的伪随机数Ri和一个64比特的新种子Vi+1。
本方案具有很高的密码强度,因为其采用了112比特长的密钥和9个DES加密,同时还由于算法由两个伪随机数输入驱动,一个是当前的日期和时间,另一个算法上次产生的新种子;而且即使某次产生的随机数Ri泄漏了,但由于Ri又经一次EDE加密后才产生新种子Vi+1,因此即使别人得到Ri也得不到Vi+1,从而得不到新随机数Ri+1。
ANSI计算模块
该模块直接完成一轮的ANSI计算,输入为三重DES加密的两个密钥(各64比特,DES使用时会自动处理)和一个用作随机种子(64比特);计算结束后返回一个随机数(64比特)和一个新的随机种子(64比特)。
void CANSIVCDlg::Ansi(char *Key1, char *Key2,char *VBuffer, char *Output)
{
char DTBuffer[8]; // 当前时间
char TempBuff[8]; // 临时数组
memset(DTBuffer,0,8);
memset(TempBuff,0,8);
// 获取当前时间,每次计算时都会改变
SYSTEMTIME st;
GetLocalTime(&st);
sprintf(DTBuffer,"%2d%2d%2d%2d",st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
// 三重DES加密
DES(DTBuffer,Key1,TRUE);
DES(DTBuffer,Key2,FALSE);
DES(DTBuffer,Key1,TRUE);
// 保存结果到临时数组
for(int i = 0; i< 8; i ++)
{
TempBuff[i] = DTBuffer[i];
}
// 将结果与初始种子进行异或运算
for(i = 0; i < 8; i ++)
{
DTBuffer[i] = DTBuffer[i] ^ VBuffer[i];
}
// 再进行一次三重DES加密
DES(DTBuffer,Key1,TRUE);
DES(DTBuffer,Key2,FALSE);
DES(DTBuffer,Key1,TRUE);
// 输出所产生的随机数(64位,8字节)
for(i = 0; i < 8; i ++)
{
Output[i] = DTBuffer[i];
}
// 下面生成新随机种子
memset(DTBuffer,0,sizeof(DTBuffer));
// 将输出结果与临时数组进行异或计算
for(i = 0; i < 8; i ++)
{
DTBuffer[i] = TempBuff[i] ^ Output[i];
}
// 进行三重DES加密
DES(DTBuffer,Key1,TRUE);
DES(DTBuffer,Key2,FALSE);
DES(DTBuffer,Key1,TRUE);
// 输出新的随机种子,下一轮加密时使用
for(i = 0; i < 8; i ++)
{
VBuffer[i] = DTBuffer[i];
}
}
生成随机数模块
因为ANSI计算函数每次只进行一轮计算,输出64比特的随机数,而系统要求能够生成长度为1024比特的随机数,因此必须使用ANSI计算函数进行连续16轮计算。
void CANSIVCDlg::OnOK()
{
// 检查编辑框内容是否合法
m_wndStatus.SetPaneText(0,"");
if(!(InputValid()))
return;
// 设置初始缓冲区
char Key1[8];
char Key2[8];
char buffer[9];
char VBuffer[8];
memset(Key1,0,8);
memset(Key2,0,8);
memset(buffer,0,8);
// 得到三重DES的两个密钥
m_edtKey1.GetWindowText(buffer,9);
memcpy(Key1,buffer,8);
m_edtKey2.GetWindowText(buffer,9);
memcpy(Key2,buffer,8);
// 设置初始种子值,初值可任意设定,以后会自动改变
sprintf(VBuffer,"12345678");
CString str;
CString Result;
// 循环进行16轮ANSI计算
for(int i = 0; i < 16; i ++)
{
Ansi(Key1,Key2,VBuffer,buffer);
str.Format("%X%X%X%X%X%X%X%X",buffer[0] & 0xF,buffer[1] & 0xF,buffer[2] & 0xF,buffer[3] & 0xF,buffer[4] & 0xF,buffer[5] & 0xF,buffer[6] & 0xF,buffer[7] & 0xF);
Result = Result + str;
}
// 输出结果
m_edtResult.SetWindowText(Result);
}
在进行16轮循环调用Ansi(Key1,Key2,VBuffer,buffer)函数时,虽然随机种子VBuffer的初值固定,但由于每次计算后生成的新随机种子同样是通过该参数输出,因此在进行下一轮计算时VBuffer中的值已经发生了改变,实现了随机性。
最近比较忙,就懒得详细整理上边的东西了,凑合着看吧,呵呵。