
| 出版日期:1999-09-06 总期号:855 本年期号:65 |
|
用delphi开发基于cryptoapi的安全加密应用
陈耀光 本文就“如何使用delphi开发基于cryptoapi的安全加密应用”进行了基本的展示,并以wps2000应用cryptoapi对其.wps文件进行加密为例,作了更详尽的说明。 microsoft cryptoapi是“microsoft(r) cryptographic application programming interface”的简称,是microsoft公司在windows 9x/nt平台上推出的安全加密应用体系和服务。在此基础之上,应用开发者可以比较简便、快速地开发出标准、通用和易于扩展的安全加密应用。本文将就“如何使用delphi开发基于cryptoapi的安全加密应用”进行了基本的展示,并结合wps2000在其.wps文件的加密中对cryptoapi的应用作为示例、说明(注:关于wps2000的文件加密方式及相关资料仅供参考,如有出入,以金山公司为准)。 cryptoapi概要 microsoft cryptoapi 2.0的体系结构如图1所示。microsoft cryptoapi 2.0的通用加密和解密过程如图2 所示。 其实,对于大部分的应用开发者来说,最多只需要知道其中大约三分之一的api函数接口即可完成安全加密应用开发的工作。下面就一步一步以示例的方式向大家展示如何使用基本的api实现加密和解密的过程,大家有了感性认识之后,再回头看其整体结构和其他细节,可能会容易理解一些。 由于microsoft win32 sdk中关于cryptoapi的编程范例都是采用c/c++语言书写的,为了方便广大的delphi爱好者们的阅读和引用,以下的cryptoapi程式片段均采用delphi语法来示例,并在delphi 4.0上实际调试通过,有兴趣者尽可一试! 在正式开始使用delphi开发cryptoapi安全加密应用之前,需要我们先把win32 sdk中的wincrypt.h文件中的常量定义、函数声明等转换为delphi所识别的格式,或希望inprise公司在8月底上市的delphi 5.0中能提供。 加密过程 第一步:初始化加密上下文(cryptographic context),需要调用cryptacquirecontext函数。 一般地,对于绝大多数的microsoft cryptoapi的函数,如果返回值rc为false时,表示该函数执行失败,可以调用getlasterror函数获取进一步的错误代码;如果rc为true,则操作成功,可以继续下一步的操作。 第二步:初始化加密用的散列句柄,需要调用cryptcreatehash函数。
图1 microsoft cryptoapi 2.0的体系结构
图2 microsoft cryptoapi 2.0的通用加密和解密过程 第三步:用加密密码生成散列数据,需要调用crypthashdata函数。 第四步:用散列数据生成加密会话键(session key)。加密会话键是实际用于进行加密和解密操作的密码,由于加密和解密是使用同样的加密会话键,所以只要能获取此密码即可直接对数据进行解密,而无须知道最终用户的真正的原始加密密码!所以此加密会话键必须妥善处理好。一般地,应该在使用它完成加密操作之后就彻底废弃掉,这样,最终用户必须拥有其原始加密密码才可能重新生成该加密会话键。wps2000的“密码设置”中的“绝密型加密”方式就是如此,所以其安全系数是相当高的,如果不小心忘掉了原始密码的话,只能采用穷举的方法找寻加密密码了! 看到这里,细心的读者可能会问:既然wps2000的“绝密型加密”是如此实现的,那么,wps2000的另一种密码设置方式“普通性加密”会不会就意味着它把加密会话键也存放到加密后的.wps文件中,从而在必要时可以使用它来进行数据恢复呢? wps2000并没有把加密会话键直接地、原封不动地保存到文件里。它使用了金山公司的一个公开密钥进行了加密。 好!回到delphi开发cryptoapi应用的主题来,调用cryptderivekey函数。 第五步:终于到实际的加密操作了!用加密会话键加密数据块,需要调用cryptencrypt函数。 一个典型的连续多个数据块的加密流程如下: fs, fs2 : tfilestream; // 输入输出的文件指针,文件的打开、创建的代码从略. while true do begin len := fs.read(buf, 160); // 从需要加密的文件中读取一个数据块,这里的数据块长度为160个字节(它与所采用的加密算法有关) if len <= 0 then // 如果文件结束,则退出循环 break; if len < 160 then // 如果剩余的数据块大小不足一块,则说明这是最后的一个数据块,需要设置final标志为true final := true; myencryptblock(buf, len); // 对数据块进行加密 fs2.write(buf, len); // 将加密后的数据块输出到加密文件之中 end; 当然,在实际的应用中,还需要与应用的其它数据块一起结合起来使用。比如说,在wps2000的文件加密中,无论该.wps文件是否加密,都有一个长度为96个字节的文件头。是否加密的唯一区别在于文件头的偏移$50(十六进制,以下同)处的一个字节。如果是$00的话,则该文件为没有加密;如果为$60的话,则为加密文件。如果是加密文件,则在文件头结束后的第一字节即文件偏移$60处的一个字节表示加密的方式。如果是$0c1,则为“普通型加密”方式;如果为$0c2,则为“绝密型加密”方式;如果为其它则为非法文件格式。正如上文所提到的,如果为“普通型加密”方式,则接着存放4个字节的加密会话键长度的数值(为$4c)和76个字节的加密会话键的内容本身(其中的64个字节即512bit为实际的加密后的key,具体可以查看cryptoapi手册中的数据结构定义),然后是与“绝密型加密”同样的加密后的数据块,其中,第一个加密数据块存放的是原始密码加密后的密文,为160个字节(所以,金山公司可能知道该文件的原始密码喔!),余下的数据块的大小与未加密的原文的大小一样,只不过是以160个字节为单位进行了加密而已。 第六步:最后,清理现场即可。 解密过程 解密过程与加密过程基本类似,简介如下: 第一步:初始化加密上下文(cryptographic context),调用cryptacquirecontext函数。 第二步:初始化加密用的散列句柄,调用cryptcreatehash函数。 第三步:用最终用户输入的原始解密密码生成散列数据,调用crypthashdata函数。 第四步:用散列数据生成加密会话键(session key)。 第五步:用加密会话键解密数据块,需要调用cryptdecrypt函数。 相应地,对于wps2000来说,第一个数据块解密出来的字符串即为加密所用的明文形式的原始密码。如果它与最终用户输入的原始解密密码不一样的话,说明要么是输入的密码不对,要么是加密文件损坏了。 第六步:最后,同样地,清理现场即可。调用cryptdestroykey、cryptdestroyhash和cryptreleasecontext等函数。 如果采用类似于wps2000的“普通型加密”方式加密的话,则上述生成session key的过程需要改为如下的步骤: 首先,要初始化用于解出“加密会话键”的私有密钥,需要调用cryptimportkey函数。 然后,解出“加密会话键”来,需要再次调用函数cryptimprotkey。 结束语 以上的加密过程和解密过程示例只是cryptoapi 2.0中最基本、最常用到的功能,进一步深入的应用,如密钥的管理、数字签名、身份认证、smartcard接口等方面,都是电子商务应用中的重要组成部分,感兴趣的读者需要继续深入研究cryptoapi。 (注:文中所调用的一切函数因版面所限不能刊出,我们将在infocd10中提供。如需要者,敬请关注infocd10 的程序特供。) |
|||||||||||||||||