一、c语言中的算法采用线性同余算法产生,首先要设置seed(),线性同余算法的随机性很差,不能满足加密的要求
二、windows下随机数的生成:
有两种方法可以生产随机数,包括:线性适配随机发生器和不可预测随机数发生器。
线性适配随机发生器:
在许多程序设计中,都简单地调用了相关的随机函数。比如windows下Microsoft Visual C++6.0 中的 rand(),在C 运行库(C Run Time lib,CRT)中,其定义类似如下(为了简洁起见,n省略了多线程程序部分等)。
int holdrand=1;
int _cdecl rand(void)
{ return ((holdrand=holdrand*214013L+25310112)>>16)&0x7fff}; }
该类型的函数被称为线性适配函数(linear congruential function)。 此类函数的一个的缺点就是可预测性,因为上一个随机数是下个随机数的种子,具有很强的相关性。其中两次与之相关的重要事件有:
① Netscape Navigator浏览器早期版本的攻击可能是最著名的可预测随机攻击,其中用于其SSL(Secure Sockets Layer,安全套接字层)密钥的随机数有着很高的可预测性,使得SSL 失去意义。
② 另一个就是攻击ASF软件公司的TexasHoldem Poker应用程序。这种“发牌”软件在算法中利用了Borland Delphi的随机函数。这个随机函数是类似于上面提及的CRT中的简单线性适配函数:rand()函数。
2. 不可预测随机数发生器
2.1 随机数发生器特性
一个好(或称健壮)的随机数发生器有着一下以下三个特性: ① 产生平均的数; ② 数据不可预测; ③ 取值范围长且完整(即:它能产生大量的不同值的随机数,而且在取值范围内的所有数值能够被生成)。
线性适配函数产生的随机数,仅满足第一个特性,第二个特性非常糟糕。换言之,rand()
函数可以生成一系列平均分布的数,但每一个数却有很强的可预测性。
2.2 不可预测随机发生器设计
在Windows环境中,一个健壮的随机函数是:CryptGenRandom()。它适用于Windows XP, Windows 2000 Professional, Windows NT Workstation 4.0, Windows Me, Windows 98, or Windows 95 OSR2 或更高版本,Windows Server 2003, Windows 2000 Server, or Windows NT Server 4.0及 Internet Explorer 3.02 or later on Windows 95,定义在Wincrypt.h.
CryptGenRandom从Windows2000的众多的资源中,获得其随机性[也称作“熵”(entropy)]:①当前进程的ID;②当前线程的ID;③系统引导以来的时钟数;④各种高精度的性能计数器;⑤用户环境模块的MD4(Message Digest 4,信息摘要4)散列,包括用户名,计算机名和搜索路径等;⑥高精度的内部CPU计算器,如RDISC,ROMSR,RDPM等;⑦底层系统信息,如空闲时间,内检时刻,中断时间,提交限定,页面计数,缓存计数,操作系统外部计数等。
(1) CryptGenRandom()函数的一般使用
①首先通过CryptAcquireContext 获取一个CSP (cryptographic service provider)提供的句柄,定义如下:
BOOL WINAPI CryptAcquireContext(
HCRYPTPROV* phProv,
LPCTSTR pszContainer,
LPCTSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags
)
参数说明:
phProv : 一个密码的服务提供者(cryptographic service provider,CSP)的句柄指针。
PszContainer:关键字容器的名字。当 dwFlags 被设为 CRYPT_VERIFYCONTEXT 时, pszContainer 必须被设为 0。一般 pszContainer 为NULL时,一个缺省钥匙容器名字被使用。例如,微软基本密码提供者(Microsoft Base Cryptographic Provider)的用户使用其当前登录名字当前作为关键字容器名字登录。
PszProvider::指定的CSP提供者的名字,是以0结尾的字符串。如果这个参数是空的,用户缺省供应商被使用。
DwProvType:指定CSP提供者的类型,取值可如下。
PROV_RSA_FULL PROV_SSL
PROV_RSA_SIG PROV_EC_ECDSA_SIG
PROV_DSS PROV_EC_ECNRA_SIG
PROV_DSS_DH PROV_EC_ECDSA_FULL
PROV_FORTEZZA PROV_EC_ECNRA_FULL
PROV_MS_EXCHANGE PROV_SPYRUS_LYNKS
PROV_RSA_SCHANNEL
其中,PROV_RSA_FULL 标识,表示提供者同时支持数字签名(Digital signatures)和数据加密(Data encryption),为通用的一般的设置(其他的具体含义,可参看MSDN)。
DwFlags: 标志值。这个参数通常被设为零。 但是应用程序也可以设置至少一个下列标志: CRYPT_VERIFYCONTEXT,CRYPT_NEWKEYSET,CRYPT_MACHINE_KEYSET,CRYPT_DELETEKEYSET(其具体含义,可参看MSDN)。
返回值:如果函数调用成功,返回值是非零 (TRUE );反之,为0(FALSE)。
②调用CryptGenRandom()函数,同时将产生的随机数据填充到缓存区中。
BOOL WINAPI CryptGenRandom(
HCRYPTPROV hProv,
DWORD dwLen,
BYTE* pbBuffer
);
参数说明:
hProv :是一个由CSP(cryptographic service provider)句柄,它由CryptAcquireContext函数调用获得的。
dwLen :缓存区的大小。
pbBuffer :存放返回随机数据的缓存区。
返回值:
调用成功则返回 TRUE;反之为FALSE。
③使用完毕释放句柄和关键字容器:CryptReleaseContext()。
调用CryptReleaseContext将释放一个CSP 句柄;同时,每调用一次,引用计数(Reference count)将减1,当引用计数为零时,则CSP上下文(context)将被彻底释放,不再能被其他应用所使用。其定义如下:
BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags
);
参数说明:
hProv :欲释放的一个CSP 句柄
dwFlags :保留使用,一般设为0。
返回值:调用成功则返回非零(TRUE)。
(2)随机数发生器设计
①简单的随机发生器,我们可以按照上述的说明,一般常设计成一个可调用函数,如下类似,不妨命名为MyCryptRandom:
#include “windows.h”
#inclucde “wincrypt.h”
BOOL MyCryptRandom(void *lpBuffer,DWORD dwLen)
{ HCRYPTPROV hCryptProv;
if(!CryptAcquireContext(&hCrptProv,NULL,NULL,PROV_RSA_FULL,0))return FALSE;
if(hCryptProv!=NULL)
{
if (!CryptGenRandom(hCryptProv,dwLen,reinterpret_cast<LPBYTE>lpBuffer))return FALSE;
}
if(hCryptProv!=NULL)CryptReleaseContext(hCryptProv,0);
return TRUE;
}
②由于随机发生器,常常可能被多次调用。而每调用一次,将获取和释放CSP 句柄,这样很显然降低了运行的效率。因此将其设计成一个类(class),仅在构造函数中,调用CryptAcquireContext函数,在构析函数中调用CryptReleaseContext函数。这样在应用时,通过该类的一个对象(Object),可多次调用随机发生函数。以下是该类一个示例。
#include “windows.h”
#inclucde “wincrypt.h”
class CCryptRandom
{
public:
BOOL Random(void *lpBuffer,DWORD dwLen);
CCryptRandom();
virtual~CCryptRandom();
private:
HCRYPTPROV hCryptProv;
};
CCryptRandom::CCryptRandom()
{
hCryptProv=NULL;
CryptAcquireContext((HCRYPTPROV*)&hCryptProv,NULL,NULL,PROV_RSA_FULL,0);
}
CCryptRandom::~CCryptRandom()
{if(hCryptProv!=NULL)CryptReleaseContext(hCryptProv,0);
}
BOOL CCryptRandom::Random(void *lpBuffer, DWORD dwLen)
{ if(hCryptProv==NULL)return FALSE;
BOOL bRet=CryptGenRandom(hCryptProv,dwLen,(BYTE*)lpBuffer);///reinterpret_cast<LPBYTE>lpBuffer);
return bRet;
}
3 结束语
由于随机数的广泛使用,其重要性是不言而喻的,当然一个健壮的随机随机发生器的设计涉及方方面面,本文权当抛砖引玉。图1(第17页)是作者做的两次独立测试(即:两次独立分别执行,然后关闭测试程序)的结果,线性适配随机函数的可预测性显然可见。