王朝网络
分享
 
 
 

学习CRYPTOAPI第一天

王朝other·作者佚名  2006-01-09
宽屏版  字体: |||超大  

一:准备工作

一般必须包含如下头文件以及定义

#include

#include

#include

#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

当包含wincrypt.h头文件时,一般都需要定义#define _WIN32_WINNT 0x(具体的值),否则将得到如下错误:error C2065: undeclared identifier,不同的操作系统不同定义如下:

Windows Server 2003 family _WIN32_WINNT>=0x0502

WINVER>=0x0502

Windows XP _WIN32_WINNT>=0x0501

WINVER>=0x0501

Windows 2000 _WIN32_WINNT>=0x0500

WINVER>=0x0500

Windows NT 4.0 _WIN32_WINNT>=0x0400

WINVER>=0x0400

Windows Me _WIN32_WINDOWS=0x0500

WINVER>=0x0500

Windows 98 _WIN32_WINDOWS>=0x0410

WINVER>=0x0410

Windows 95 _WIN32_WINDOWS>=0x0400

WINVER>=0x0400

Internet Explorer 6.0 _WIN32_IE>=0x0600

Internet Explorer 5.6 _WIN32_IE>=0x0560

Internet Explorer 5.01, 5.5 _WIN32_IE>=0x0501

Internet Explorer 5.0, 5.0a, 5.0b _WIN32_IE>=0x0500

Internet Explorer 4.01 _WIN32_IE>=0x0401

Internet Explorer 4.0 _WIN32_IE>=0x0400

Internet Explorer 3.0, 3.01, 3.02 _WIN32_IE>=0x0300

二:了解基本知识

CryptoAPI的配置信息存储在注册表中,包括如下密钥:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \ Cryptography \Defaults

HKEY_CURRENT_USER\ Software \ Microsoft\ Cryptography \Providers

---- CryptoAPI使用两种密钥:会话密钥与公共/私人密钥对。会话密钥使用相同的加密和解密密钥,这种算法较快,但必须保证密钥的安全传递。公共/私人密钥对使用一个公共密钥和一个私人密钥,私人密钥只有专人才能使用,公共密钥可以广泛传播。如果密钥对中的一个用于加密,另一个一定用于解密。公共/私人密钥对算法很慢,一般只用于加密小批数据,例如用于加密会话密钥。CryptoAPI支持两种基本的编码方法:流式编码和块编码。流式编码在明码文本的每一位上创建编码位,速度较快,但安全性较低。块编码在一个完整的块上(一般为64位)上工作,需要使用填充的方法对要编码的数据进行舍入,以组成多个完整的块。这种算法速度较慢,但更安全。

三:下面进入具体的编程

一: Creating a Key Container and Generating Keys

创建一个密钥容器,在进行加密,解密文件,并且签名的时候,必须需要一个公/私钥对,下面我们就来创建默认的密钥容器,要注意的是创建密钥容器并不会自动产生公/私钥对.

下面是我们程序的任务:

1,假如密钥容器不存在则创建一个。

2,假如签名密钥不存在则在密钥容器里创建一个。

3,假如交换密钥不存在则在密钥容器里创建一个。

4,获取CSP中的一些参数

下面是具体的步骤:

1,连接缺省的CSP

BOOL WINAPI CryptAcquireContext(

HCRYPTPROV* phProv, //out

LPCTSTR pszContainer, //in

LPCTSTR pszProvider, //in

DWORD dwProvType, //in

DWORD dwFlags //in

);

第一个参数是返回的CSP句柄,第二个是密钥容器的名字,第三个是A null-terminated string that specifies the name of the CSP to be used.第四个是指定提供的类型。例如:CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0));

如果当前机器的未曾设置过缺省的密钥容器,因此必须为机器创建缺省的密钥容器。

CryptAcquireContext( &hCryptProv, UserName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)大家有没有看到,只是最后一个参数不同而已,多了一个CRYPT_NEWKEYSET而已。

2,取得CSP的参数

BOOL WINAPI CryptGetProvParam(

HCRYPTPROV hProv,

DWORD dwParam,

BYTE* pbData,

DWORD* pdwDataLen,

DWORD dwFlags

);

第一个参数是CSP的句柄,第二个参数是需要取得的具体参数对象(类型比较多,具体请看MSDN)。

例子:CryptGetProvParam(hCryptProv, PP_CONTAINER, (BYTE *)szUserName, &dwUserNameLen, 0)

3,函数返回所获取密钥类型的句柄(0表失败,非0表成功)

BOOL WINAPI CryptGetUserKey(

HCRYPTPROV hProv,

DWORD dwKeySpec,

HCRYPTKEY* phUserKey

);

参数比较简单,只谈谈第二次参数,它可以是AT_KEYEXCHANGE(交换密钥) or AT_SIGNATURE(签名密钥),例如:

CryptGetUserKey(hCryptProv,AT_KEYEXCHANGE,&hKey)

4,产生一个随机的交换密钥或者公/私钥对

BOOL WINAPI CryptGenKey(

HCRYPTPROV hProv,

ALG_ID Algid,

DWORD dwFlags,

HCRYPTKEY* phKey

);

ALG_ID 表明产生私钥所使用的算法。有如下参数:

微软提供的基本算法

CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_MAC,CALG_HMAC,CALG_SSL3_SHAMD5,CALG_MD2,CALG_MD2

CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES

微软提供的增强型算法:

CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1

CALG_MAC,CALG_HMAC ,CALG_SSL3_SHAMD5,CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES,CALG_3DES_112,CALG_3DES

使用DH的CSP有如下两个参数,CALG_DH_EPHEM,CALG_DH_SF

使用公开密钥算法:AT_KEYEXCHANGE,AT_SIGNATURE

dwFlags,,表示密钥使用的长度,参数可以为0,采用默认的密钥长度。或者是进行如下几个参数的或:

CRYPT_ARCHIVABLE:表示在句柄在关闭之前都能够被导出

CRYPT_CREATE_SALT:表示密钥按照一个salt value来随机产生。

CRYPT_EXPORTABLE:表示密钥可以从CSP中导出到BLOB,因为会话密钥产生是必须可导出的,所以必须设置

CRYPT_NO_SALT:表示没有SALT VALUE获取allocated for a forty-bit symmetric key

CRYPT_PREGEN:表示在DH或者DSS密钥产生必须有个初始化。

例如:CryptGenKey(hCryptProv,AT_KEYEXCHANGE,0,&hKey)

5,释放CSP句柄

BOOL WINAPI CryptReleaseContext(

HCRYPTPROV hProv,

DWORD dwFlags //保留字,现在必须为0

);

6,为CSP增加一个reference count(用来跟踪COM对象的整数值,当对象创建,值为1。每次对对象的操作都将增加,而对对象的关闭将减少,当值为0是,对象释放,所以与对象相关操作将无效)

BOOL WINAPI CryptContextAddRef(

HCRYPTPROV hProv,

DWORD* pdwReserved, //保留字,必须为NULL

DWORD dwFlags //保留字,必须为0

);

二:Deriving a Session Key from a Password

1,连接CSP

2,使用CryptCreateHash产生一个空的HASH对象

3,对密码进行HASH处理

4,释放HASH以及密码对象

5,释放CSP

下面是具体的步骤:

1,CryptCreateHash初始化一个HASH对象

BOOL WINAPI CryptCreateHash(

HCRYPTPROV hProv, //in

ALG_ID Algid, //in

HCRYPTKEY hKey, //in

DWORD dwFlags, //in保留字,必须为0

HCRYPTHASH* phHash //out

);

第二个参数是指定HASH算法,有CALG_HMAC,CALG_MAC,CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_SSL3_SHAMD5。第三个参数对于那些keyed hash,例如HMAC,MAC算法。但是nonkeyed算法,必须设置为0。

2,CryptHashData对数据使用HASH

BOOL WINAPI CryptHashData(

HCRYPTHASH hHash, //in,HASH对象句柄

BYTE* pbData, //in,待HASH的数据

DWORD dwDataLen, //in,待HASH数据的长度,当dwFlags为CRYPT_USERDATA为0时,必须为0

DWORD dwFlags //in,一般为0,或者为CRYPT_USERDATA(用在用户进入系统时需要输入PIN)

);

3,CryptDeriveKey从某一数据产生会话密钥,他有点类似CryptGenKey,但是他产生的会话密钥来自固定数据,而CryptGenKey是随机产生的。并且不能产生公/私钥对

BOOL WINAPI CryptDeriveKey(

HCRYPTPROV hProv, //in,CSP句柄

ALG_ID Algid, //in,指定的算法,类似CryptGenKey

HCRYPTHASH hBaseData, //in,HASH对象的句柄

DWORD dwFlags, //in,指定产生密钥的类型

HCRYPTKEY* phKey //in,out产生的密钥句柄地址

);

例如:CryptDeriveKey(hCryptProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)

4,CryptDestroyHash(hHash);

5, CryptDestroyKey(hKey);

6, 在这里发现一个不错的函数,就是那种提示输入密码的命令行(屏幕只会出现***)

void GetConsoleInput(char* strInput, int intMaxChars)

{

char ch;

char minChar = ' ';

minChar++;

ch = getch();

while (ch != '\r')

{

if (ch == '\b' && strlen(strInput) > 0)

{

strInput[strlen(strInput)-1] = '\0';

printf("\b \b");

}

else if (ch >= minChar && strlen(strInput) < intMaxChars)

{

strInput[strlen(strInput)+1] = '\0';

strInput[strlen(strInput)] = ch;

putch('*');

}

ch = getch();

}

putch('\n');

}

三:Duplicating,setting and getting Session key

1,连接CSP

2,使用CryptGenKey产生一个会话密钥

3,CryptDuplicateKey复制会话密钥

4,CryptSetKeyParam改变密钥产生的过程

5,CryptGenRandom产生随机数

具体过程。

1,CryptDuplicateKe复制会话密钥

BOOL WINAPI CryptDuplicateKey(

HCRYPTKEY hKey, //in 会话密钥句柄

DWORD* pdwReserved, //in 保留字,必须为NULL

DWORD dwFlags, //in 保留字,必须为0

HCRYPTKEY* phKey //out 新的会话密钥

);

2,CryptSetKeyParam定制会话密钥的参数

BOOL WINAPI CryptSetKeyParam(

HCRYPTKEY hKey,

DWORD dwParam, //in 很多,具体请看MSDN

BYTE* pbData,

DWORD dwFlags //in 只有在dwParam=KP_ALGID才被使用

);

例如CryptSetKeyParam(hOriginalKey, KP_MODE, (BYTE*)&dwMode, 0)(dwMode = CRYPT_MODE_ECB)

3,CryptGenRandom为空间产生随机字节

BOOL WINAPI CryptGenRandom(

HCRYPTPROV hProv,

DWORD dwLen, //需要产生的随机比特数

BYTE* pbBuffer //需要返回数据的空间,这个pbBuffer可以等于CryptSetKeyParam的pbData

);

例如:CryptGenRandom(hCryptProv, 8, pbData)

4,CryptGetKeyParam获取密钥的一些参数

BOOL WINAPI CryptGetKeyParam(

HCRYPTKEY hKey,

DWORD dwParam, //in,参数众多

BYTE* pbData, //out,获取BYTE数据的指针

DWORD* pdwDataLen, //out,获取BYTE数据的长度

DWORD dwFlags //关键字,必须为0

);

例如:CryptGetKeyParam(hKey, KP_IV, pbData, &dwCount, 0)

四:Exporting a Session Key

1,连接CSP

2,CryptGetUserKey获取公/私钥对和交换密钥,公私钥用来签名,而交换密钥用来导出会话密钥

3,CryptGenKey产生会话密钥

4,CryptExportKey创建简单包含有会话密钥的key BLOB

5,释放处理:

具体过程:

1,CryptExportKey函数导出密钥

BOOL WINAPI CryptExportKey(

HCRYPTKEY hKey, //需要导出的密钥句柄

HCRYPTKEY hExpKey, //将待导出密钥用交换密钥进行加密,假如是公开的BLOG当然就设置为0

DWORD dwBlobType, //指定导出的密钥BLOB类型。六个参数见MSDN

DWORD dwFlags, //CRYPT_DESTROYKEY,CRYPT_SSL2_FALLBACK,CRYPT_OAEP

BYTE* pbData, //导出的数据指针,以后就可以将这个数据写如磁盘或者别的任务。

DWORD* pdwDataLen //导出的数据长度

);

例如:CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen)

2,CryptImportKey将密钥从BLOB转换到CSP中

BOOL WINAPI CryptImportKey(

HCRYPTPROV hProv, //CSP句柄

BYTE* pbData, //待转换的BLOB数据

DWORD dwDataLen, //待转换的数据长度

HCRYPTKEY hPubKey, //对BLOB解密的公钥,譬如上面是用交换密钥密钥加密的,就用交换密钥解密

DWORD dwFlags, //目前还只应用在当一对公/私钥从PRIVATEKEYBLOB中加入CSP中这种情况。

HCRYPTKEY* phKey //out导入的密钥

);

例如:CryptImportKey(hProv,pbKeyBlob,dwBlobLen,0,0,&hPubKey)

五:Encoding and Decoding Messages

编码的处理过程

1,将待编码的数据转化为合适的格式,使用

2,调用CryptMsgOpenToEncode,passing the necessary argument;

3, 调用CryptMsgUpdate函数多次,最后一次调用时,将final参数设置为true

4, 调用CryptMsgGetParam来获取一个需要得到的参数。

5, 调用CryptMsgClose来关闭消息

解码的处理过程

1,检查申请的放编码后数据的空间,利用函数CryptMsgCalculateEncodedLength.

2,调用函数CryptMsgOpenToDecode,passing the necessary argument;

3,调用CryptMsgUpdate一次,这将导致合适的动作去处理信息,以来于信息的格式

4,一些额外的处理,例如额外的解密或者是验证,调用CryptMsgControl,

5,调用CryptMsgGetParam来获取需要得到的参数

6,调用CryptMsgClose来关闭消息

具体的函数介绍:

1,CryptMsgCalculateEncodedLength计算所需要的存储编码的最大空间值

DWORD WINAPI CryptMsgCalculateEncodedLength(

DWORD dwMsgEncodingType,//指定编码类型。一般为X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

DWORD dwFlags,

DWORD dwMsgType,

const void* pvMsgEncodeInfo, //in 指向待编码的数据,数据类型依赖于dwMsgType

LPSTR pszInnerContentObjID,

DWORD cbData //in 比特数的容量

);

第二个参数:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG

第三个参数:CMSG_DATA,CMSG_SIGNED,CMSG_ENVELOPED,CMSG_SIGNED_AND_ENVELOPED,CMSG_HASHED,CMSG_ENCRYPTED

第五个参数:szOID_RSA_data,szOID_RSA_signedData,szOID_RSA_envelopedData,szOID_RSA_signEnvData,szOID_RSA,digestedData ,

szOID_RSA_encryptedData,SPC_INDIRECT_DATA_OBJID,NULL

返回值:返回需要的一个加密信息所需要的长度

2,CryptMsgOpenToEncode打开一个消息以便进行编码,返回打开消息的句柄

HCRYPTMSG WINAPI CryptMsgOpenToEncode(

DWORD dwMsgEncodingType, //指定编码类型。一般为X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

DWORD dwFlags,

DWORD dwMsgType,

const void* pvMsgEncodeInfo,

LPSTR pszInnerContentObjID, //和CryptMsgCalculateEncodedLength一样

PCMSG_STREAM_INFO pStreamInfo //当流没被使用时,该参数为NULL

);

第二个参数:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG,

CMSG_CRYPT_RELEASE_CONTEXT_FLAG

第三个参数:

CMSG_DATA(Not used),CMSG_SIGNED,CMSG_SIGNED_ENCODE_INFO,CMSG_ENVELOPED,CMSG_ENVELOPED_ENCODE_INFO

CMSG_SIGNED_AND_ENVELOPED(Not currently implemented),CMSG_HASHED

3,CryptMsgOpenToDecode打开一个消息以便进行解码,返回打开消息的句柄

CRYPTMSG WINAPI CryptMsgOpenToDecode(

DWORD dwMsgEncodingType, //指定编码类型。一般为X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

DWORD dwFlags, //CMSG_DETACHED_FLAG,CMSG_CRYPT_RELEASE_CONTEXT_FLAG

DWORD dwMsgType, //CMSG_DATA,CMSG_ENVELOPED,CMSG_HASHED,CMSG_SIGNED,CMSG_SIGNED_AND_ENVELOPED

HCRYPTPROV hCryptProv, //指定使用HASHING的句柄,一般设置为0

PCERT_INFO pRecipientInfo,//保留字,必须为NULL

PCMSG_STREAM_INFO pStreamInfo//假如流没被使用,必须为NULL

);

4,CryptMsgUpdate增加内容到加密信息中

BOOL WINAPI CryptMsgUpdate(

HCRYPTMSG hCryptMsg, //待更新的加密信息句柄

const BYTE* pbData, //待编码/解码的数据

DWORD cbData, // pbData 的数据长度

BOOL fFinal

);

第四个参数:当CMSG_DETACHED_FLAG没有设置,并且信息由CryptMsgOpenToDecode或CryptMsgOpenToEncode打开,那么fFinal被设置为TRUE,并且CryptMsgUpdate只被调用一次。当CMSG_DETACHED_FLAG被设置,并且信息由 CryptMsgOpenToEncode打开,那么仅在最后一次调用CryptMsgUpdate才被设置为TRUE。当CMSG_DETACHED_FLAG被设置,并且信息由CryptMsgOpenToDecode打开,那么仅在信息头单独被处理时CryptMsgUpdate才被设置为TRUE。

5,CryptMsgGetParam在数据编码/解码后获取参数

BOOL WINAPI CryptMsgGetParam(

HCRYPTMSG hCryptMsg, //in 信息句柄

DWORD dwParamType, //in 参数众多,参见MSDN

DWORD dwIndex, //in 可适用的返回参数句柄,假如参数没有被获取,则被忽略或则为0

void* pvData, //out 获取的数据指针

DWORD* pcbData //in,out数据长度

);

6,CryptMsgClose关闭信息句柄

BOOL WINAPI CryptMsgClose(

HCRYPTMSG hCryptMsg

);

具体的例子:

cbEncodedBlob = CryptMsgCalculateEncodedLength(

MY_ENCODING_TYPE, // Message encoding type

0, // Flags

CMSG_DATA, // Message type

NULL, // Pointer to structure

NULL, // Inner content object ID

cbContent)) // Size of content

hMsg = CryptMsgOpenToEncode(

MY_ENCODING_TYPE, // Encoding type

0, // Flags

CMSG_DATA, // Message type

NULL, // Pointer to structure

NULL, // Inner content object ID

NULL)) // Stream information (not used)

CryptMsgUpdate(

hMsg, // Handle to the message

pbContent, // Pointer to the content

cbContent, // Size of the content

TRUE)) // Last call

{

CryptMsgGetParam(

hMsg, // Handle to the message

CMSG_BARE_CONTENT_PARAM, // Parameter type

0, // Index

pbEncodedBlob, // Pointer to the BLOB

&cbEncodedBlob)) // Size of the BLOB

CryptMsgClose(hMsg);

hMsg = CryptMsgOpenToDecode(

MY_ENCODING_TYPE, // Encoding type.

0, // Flags.

CMSG_DATA, // Look for a data message.

NULL, // Cryptographic provider.

NULL, // Recipient information.

NULL)) // Stream information.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有