基于ANSI X9.17的伪随机数发生器实现

时间:2022-12-03 14:43:21

去年做的密码学开发实践,其实这东西看穿了也没啥难的,别看题目起的多吓人,其实程序是简单的很,哈哈,今天懒得打字了,直接把当时的课程设计报告部分内容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中的值已经发生了改变,实现了随机性。

最近比较忙,就懒得详细整理上边的东西了,凑合着看吧,呵呵。