用CryptoAPI进行数据加密.docx
《用CryptoAPI进行数据加密.docx》由会员分享,可在线阅读,更多相关《用CryptoAPI进行数据加密.docx(9页珍藏版)》请在冰豆网上搜索。
用CryptoAPI进行数据加密
用CryptoAPI进行数据加密
南京理工大学计算机系胡静李蔚清
由于过于复杂的加密算法实现起来非常困难,所以在过去,许多应用程序只能使用非常简单的加密技术,这样做的结果就是加密的数据很容易被人破译。
而使用Microsoft提供的加密应用程序接口(即CryptographyAPI),或称CryptoAPI,可以方便地在应用程序中加入强大的加密功能,而不必考虑基本的算法。
本文将对CryptoAPI及其使用的数据加密原理作一简单的介绍,然后给出了用CryptoAPI编写加密程序的大致步骤,最后以一个文件的加密、解密程序为例演示CryptoAPI的部分功能。
1.CryptoAPI简介
CryptoAPI是一组函数,为了完成数学计算,必须具有密码服务提供者模块(CSP)。
Microsoft通过捆绑RSABaseProvider在操作系统级提供一个CSP,使用RSA公司的公钥加密算法,更多的CSP可以根据需要增加到应用中。
事实上,CSP有可能与特殊硬件设备(如智能卡)一起来进行数据加密。
CryptoAPI接口允许简单的函数调用来加密数据,交换公钥,散列一个消息来建立摘要以及生成数字签名。
它还提供高级的管理操作,如从一组可能的CSP中使用一个CSP。
此外,CryptoAPI还为许多高级安全性服务提供了基础,包括用于电子商务的SET,用于加密客户机/服务器消息的PCT,用于在各个平台之间来回传递机密数据和密钥的PFX,代码签名等等。
CryptoAPI的体系结构如图1。
图1CryptoAPI体系结构
目前支持CryptoAPI的Windows系统有:
Windows95OSR2、WindowsNTSP3及后续版本、Windows98、Windows2000等。
CryptoAPI的配置信息存储在注册表中,包括如下密钥:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults
HKEY_CURRENT_USER\Software\Microsoft\Cryptography\Providers
2.数据加密原理
数据加密流程如图2所示。
图2数据加密流程
CryptoAPI使用两种密钥:
会话密钥与公共/私人密钥对。
会话密钥使用相同的加密和解密密钥,这种算法较快,但必须保证密钥的安全传递。
公共/私人密钥对使用一个公共密钥和一个私人密钥,私人密钥只有专人才能使用,公共密钥可以广泛传播。
如果密钥对中的一个用于加密,另一个一定用于解密。
公共/私人密钥对算法很慢,一般只用于加密小批量数据,例如用于加密会话密钥。
CryptoAPI支持两种基本的编码方法:
流式编码和块编码。
流式编码在明码文本的每一位上创建编码位,速度较快,但安全性较低。
块编码在一个完整的块上(一般为64位)工作,需要使用填充的方法对要编码的数据进行舍入,以组成多个完整的块。
这种算法速度较慢,但更安全。
3.应用举例
下面以两个文件加密与解密的C程序片断为例,演示一下CryptoAPI的强大功能。
这两个程序均为Win32控制台应用,程序省略了出错处理,实际运行时请加入。
1文件加密
#include
#include
#include
#include
//确定使用RC2块编码或是RC4流式编码
#ifdefUSE_BLOCK_CIPHER
#defineENCRYPT_ALGORITHMCALG_RC2
#defineENCRYPT_BLOCK_SIZE8
#else
#defineENCRYPT_ALGORITHMCALG_RC4
#defineENCRYPT_BLOCK_SIZE1
#endif
voidCAPIDecryptFile(PCHARszSource,PCHARszDestination,PCHARszPassword);
void_cdeclmain(intargc,char*argv[])
{
PCHARszSource=NULL;
PCHARszDestination=NULL;
PCHARszPassword=NULL;
//验证参数个数
if(argc!
=3&&argc!
=4){
printf("USAGE:
decrypt[]\n");
exit
(1);
}
//读取参数.
szSource=argv[1];
szDestination=argv[2];
if(argc==4){
szPassword=argv[3];
}
CAPIDecryptFile(szSource,szDestination,szPassword);
}
/*szSource为要加密的文件名称,szDestination为加密过的文件名称,szPassword为加密口令*/
voidCAPIEncryptFile(PCHARszSource,PCHARszDestination,PCHARszPassword){
FILE*hSource=NULL;
FILE*hDestination=NULL;
INTeof=0;
HCRYPTPROVhProv=0;
HCRYPTKEYhKey=0;
HCRYPTKEYhXchgKey=0;
HCRYPTHASHhHash=0;
PBYTEpbKeyBlob=NULL;
DWORDdwKeyBlobLen;
PBYTEpbBuffer=NULL;
DWORDdwBlockLen;
DWORDdwBufferLen;
DWORDdwCount;
hSource=fopen(szSource,"rb"));//打开源文件
hDestination=fopen(szDestination,"wb");//.打开目标文件
//连接缺省的CSP
CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0));
if(szPassword==NULL){
//口令为空,使用随机产生的会话密钥加密
//产生随机会话密钥。
CryptGenKey(hProv,ENCRYPT_ALGORITHM,CRYPT_EXPORTABLE,&hKey)
//取得密钥交换对的公共密钥
CryptGetUserKey(hProv,AT_KEYEXCHANGE,&hXchgKey);
//计算隐码长度并分配缓冲区
CryptExportKey(hKey,hXchgKey,SIMPLEBLOB,0,NULL,&dwKeyBlobLen);
pbKeyBlob=malloc(dwKeyBlobLen))==NULL);
//将会话密钥输出至隐码
CryptExportKey(hKey,hXchgKey,SIMPLEBLOB,0,pbKeyBlob,&dwKeyBlobLen))
//释放密钥交换对的句柄
CryptDestroyKey(hXchgKey);
hXchgKey=0;
//将隐码长度写入目标文件
fwrite(&dwKeyBlobLen,sizeof(DWORD),1,hDestination);
//将隐码长度写入目标文件
fwrite(pbKeyBlob,1,dwKeyBlobLen,hDestination);
}else{
//口令不为空,使用从口令派生出的密钥加密文件
CryptCreateHash(hProv,CALG_MD5,0,0,&hHash);//建立散列表
CryptHashData(hHash,szPassword,
strlen(szPassword),0);//散列口令
//从散列表中派生密钥
CryptDeriveKey(hProv,ENCRYPT_ALGORITHM,hHash,0,&hKey);
//删除散列表
CryptDestroyHash(hHash);
hHash=0;
}
//计算一次加密的数据字节数,必须为ENCRYPT_BLOCK_SIZE的整数倍dwBlockLen=1000-1000%ENCRYPT_BLOCK_SIZE;
//如果使用块编码,则需要额外空间
if(ENCRYPT_BLOCK_SIZE>1){
dwBufferLen=dwBlockLen+ENCRYPT_BLOCK_SIZE;
}else{
dwBufferLen=dwBlockLen;
}
//分配缓冲区
pbBuffer=malloc(dwBufferLen);
//加密源文件并写入目标文件
do{
//从源文件中读出dwBlockLen个字节
dwCount=fread(pbBuffer,1,dwBlockLen,hSource);
eof=feof(hSource);
//加密数据
CryptEncrypt(hKey,0,eof,0,pbBuffer,&dwCount,dwBufferLen);
//将加密过的数据写入目标文件
fwrite(pbBuffer,1,dwCount,hDestination);
}while(!
feof(hSource));
printf("OK\n");
......//关闭文件、释放内存
}
2文件解密
voidCAPIDecryptFile(PCHARszSource,PCHARszDestination,PCHARszPassword)
{
......//变量声明、文件操作同文件加密程序
CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0);
if(szPassword==NULL){
//口令为空,使用存储在加密文件中的会话密钥解密
//读隐码的长度并分配内存
fread(&dwKeyBlobLen,sizeof(DWORD),1,hSource);
pbKeyBlob=malloc(dwKeyBlobLen))==NULL);
//从源文件中读隐码.
fread(pbKeyBlob,1,dwKeyBlobLen,hSource);
//将隐码输入CSP
CryptImportKey(hProv,pbKeyBlob,dwKeyBlobLen,0,0,&hKey);
}else{
//口令不为空,使用从口令派生出的密钥解密文件
CryptCreateHash(hProv,CALG_MD5,0,0,&hHash);
CryptHashData(hHash,szPassword,strlen(szPassword),0);
CryptDeriveKey(hProv,ENCRYPT_ALGORITHM,hHash,0,&hKey);
CryptDestroyHash(hHash);
hHash=0;
}
dwBlockLen=1000-1000%ENCRYPT_BLOCK_SIZE;
if(ENCRYPT_BLOCK_SIZE>1){
dwBufferLen=dwBlockLen+ENCRYPT_BLOCK_SIZE;
}else{
dwBufferLen=dwBlockLen;
}
pbBuffer=malloc(dwBufferLen);
//解密源文件并写入目标文件
do{
dwCount=fread(pbBuffer,1,dwBlockLen,hSource);
eof=feof(hSource);
//解密数据
CryptDecrypt(hKey,0,eof,0,pbBuffer,&dwCount);
//将解密过的数据写入目标文件
fwrite(pbBuffer,1,dwCount,hDestination);
}while(!
feof(hSource));
printf("OK\n");
......//关闭文件、释放内存}
正文:
给文件加密的技术很多,其中又分为不同等级,以适合不同场合的需要.这里给出最简单的文件加密技术,即采用文件逐字节与密码异或方式对
文件进行加密,当解密时,只需再运行一遍加密程序即可.
下面是一个实例程序,能对任意一个文件进行加密,密码要求用户输入,限8位以内(当然你可以再更改).程序有很好的容错设计,这是我们应该
学习的.
/*Turbo2.0pass.givefileapassword!
*/
#include
#include
#include
#include
voiddofile(char*in_fname,char*pwd,char*out_fname);/*对文件进行加密的具体函数*/
voidmain(intargc,char*argv[])/*定义main()函数的命令行参数*/
{
charin_fname[30];/*用户输入的要加密的文件名*/
charout_fname[30];
charpwd[8];/*用来保存密码*/
if(argc!
=4){/*容错处理*/
printf("nIn-fname:
n");
gets(in_fname);/*得到要加密的文件名*/
printf("Password:
n");
gets(pwd);/*得到密码*/
printf("Out-file:
n");
gets(out_fname);/*得到加密后你要的文件名*/
dofile(in_fname,pwd,out_fname);
}
else{/*如果命令行参数正确,便直接运行程序*/
strcpy(in_fname,argv[1]);
strcpy(pwd,argv[2]);
strcpy(out_fname,argv[3]);
dofile(in_fname,pwd,out_fname);
}
}
/*加密子函数开始*/
voiddofile(char*in_fname,char*pwd,char*out_file)
{
FILE*fp1,*fp2;
registercharch;
intj=0;
intj0=0;