学习CRYPTOAPI

来源:互联网 发布:windows xp系统开端口 编辑:程序博客网 时间:2024/06/08 19:02

学习CRYPTOAPI第一天

 


一:准备工作 

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

#include <malloc.h>

#include <windows.h>

#include <wincrypt.h>

 


#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 

); 

 Cryptography API 学习笔记二 收藏
前面学习了密钥容器的建立的一些知识,现在我们接下来自然是学习如何获取一个密钥,首先是获取一个session key,即对话密钥,是对称密钥。
  这些学习笔记主要是从MSDN中的例子中,学习基本的Cryptography API的用法,了解一些用法的过程。欢迎大家多提宝贵意见。
  下面依旧是从一个小程序开始。这个程序的学习任务有以下几个:
  任务一:调用CryptAcquireContext,获取一个缺省CSP和缺省密钥容器的句柄
  任务二:用CryptCreateHash去创建一个空的哈希对象
  任务三:用CryptHashData.哈希密码
任务四:调用CryptDeriveKey获取一个对话密钥
任务五:销毁密码和哈希后的数据
任务六:释放CSP
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <Wincrypt.h>

#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void GetConsoleInput(char*, int);

void main()
{

HCRYPTPROV hCryptProv;                        定义CSP句柄
HCRYPTKEY hKey;                              定义密钥句柄
HCRYPTHASH hHash;                            定义一个HASH对象的句柄
CHAR szPassword[512] = "";                       定义512大小的字符数组,用来保存密码
DWORD dwLength;                              保存密码长度
fprintf(stderr,"Enter a password to be used to create a key:");

GetConsoleInput(szPassword, 512);                  获取密码,这个是自己写的函数,目的是在屏幕上显示的是*。
printf("The password has been stored./n");
dwLength = strlen(szPassword);


if(CryptAcquireContext(                   以下是获取一个缺省的PROV_RSA_FULL  CSP 句柄
   &hCryptProv,
   NULL,
   NULL,
   PROV_RSA_FULL,
   0))
{
    printf("A context has been acquired. /n");
}
else
{
     MyHandleError("Error during CryptAcquireContext!");
}
//--------------------------------------------------------------------
if(CryptCreateHash(                                  调用CryptCreateHash创建一个HASH对象
   hCryptProv,                                     一个CSP句柄
   CALG_MD5,                                    确定哈希算法
   0,   对于非密钥算法,这个参数一定是0,如果是密钥算法,那么这个参数就是密钥
   0,                          保留参数,为0
   &hHash))                                        一个哈希对象的指针
{
    printf("An empty hash object has been created. /n");
}
else
{
    MyHandleError("Error during CryptCreateHash!");
}
//--------------------------------------------------------------------
if(CryptHashData(                                   调用CryptHashData哈希密码
   hHash,                                         哈希对象
   (BYTE *)szPassword,                 指向缓冲区的地址
   dwLength,                                      密码长度
   0))
{
     printf("The password has been hashed. /n");
}
else
{
     MyHandleError("Error during CryptHashData!");
}
//--------------------------------------------------------------------
if(CryptDeriveKey(                             调用CryptDeriveKey获取对话密码
   hCryptProv,                                CSP句柄
   CALG_RC2,                               一个ALG_ID结构,用来指定对称密钥生成的算法
   hHash,                                      哈希对象
   CRYPT_EXPORTABLE,             指定生成密钥的类型,CRYPT_EXPORTABLE意味着这个程序生成的密钥可以被其它程序调用,而不是仅仅限于这个程序当中。但是它不能用于非对称密码中。
   &hKey))
{
    printf("The key has been derived. /n");
}
else
{
    MyHandleError("Error during CryptDeriveKey!");
}
if(hHash)                                           销毁哈希对象
{
   if(!(CryptDestroyHash(hHash)))
       MyHandleError("Error during CryptDestroyHash");
}

if(hKey)                                            销毁密钥句柄
{
    if(!(CryptDestroyKey(hKey)))
        MyHandleError("Error during CryptDestroyKey");
}

if(hCryptProv)                                       销毁CSP句柄
{
   if(!(CryptReleaseContext(hCryptProv, 0)))
       MyHandleError("Error during CryptReleaseContext");
}
printf("The program to derive a key completed without error. /n");
} // end main
void MyHandleError函数的实现省略,在上一篇中有
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');
}

下面讲讲如何复制一个对话密钥,照例是从一个程序讲起,完成以下任务。获取一个CSP句柄,创建一个对话密钥,复制密钥。改变密钥生成过程,随机填充一个缓冲区,销毁密钥句柄,释放CSP句柄。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void main()
{
//-------------------------------------------------------------------
// Declare and initialize variables.

HCRYPTPROV   hCryptProv;                         CSP句柄
HCRYPTKEY    hOriginalKey;               源密钥句柄
HCRYPTKEY    hDuplicateKey;               复制后的密钥句柄
DWORD        dwMode;                           
BYTE          pbData[16];

printf("This program creates a session key and duplicates /n");
printf("that key. Next, parameters are added to the original /n");
printf("key. Finally, both keys are destroyed. /n/n");

if(CryptAcquireContext(                            获取CSP句柄,前面叙述过了,这里就不叙述了
   &hCryptProv,
   NULL,
   NULL,
   PROV_RSA_FULL,
   0))
{   
    printf("CryptAcquireContext succeeded. /n");
}
else
{
    MyHandleError("Error during CryptAcquireContext!/n");
}
//-------------------------------------------------------------------
if (CryptGenKey(                                      生成一个CALG_RC4算法生成的密钥,保存在hOriginalKey中
     hCryptProv,
     CALG_RC4,                                    ALG_ID结构,指定生成这个密钥使用的算法
     0,
     &hOriginalKey))
{
   printf("Original session key is created. /n");
}
else
{
   MyHandleError("ERROR - CryptGenKey.");
}

if (CryptDuplicateKey(                                复制密钥
     hOriginalKey,                                  源密钥
     NULL,                                        保留参数,必须为NULL
     0,                                            保留参数,必须为0
     &hDuplicateKey))                               副本密钥
{
   printf("The session key has been duplicated. /n");
}
else
{
   MyHandleError("ERROR - CryptDuplicateKey");
}
给源密钥设置附加参数
dwMode = CRYPT_MODE_ECB;                        CRYPT_MODE_ECB是一个没有反馈的块加密模式 
if(CryptSetKeyParam(
   hOriginalKey,
   KP_MODE,                   指定密钥的某种属性被改变,
KP_MODE意味着改变的是加密模式
(BYTE*)&dwMode,                  指向一个已经被初始化的缓冲区
   0))                                         
{
     printf("Key Parameters set. /n");
}
else
{
     MyHandleError("Error during CryptSetKeyParam.");
}
if(CryptGenRandom(                             随机填充一块缓冲区
   hCryptProv,                                  CSP句柄
   8,                        缓冲区大小
   pbData))                      缓冲区地址
{
     printf("Random sequence generated. /n");
}
else
{
     MyHandleError("Error during CryptGenRandom.");
}
if(CryptSetKeyParam(                             再次给密钥设置属性
   hOriginalKey,
     KP_IV,               KP_IV意味着,这个函数的第三个参数指向一个BYTE数组,数组大小为块大小/8。
   pbData,
   0))
{
     printf("Parameter set with random sequence as "
     "initialization vector. /n");
}
else
{
     MyHandleError("Error during CryptSetKeyParam.");
}
//-------------------------------------------------------------------

if (hOriginalKey)                                   以下依次销毁,释放源密钥句柄,副本句柄,CSP句柄,
    if (!CryptDestroyKey(hOriginalKey))
        MyHandleError("Failed CryptDestroyKey/n");

if (hDuplicateKey)
    if (!CryptDestroyKey(hDuplicateKey))
            MyHandleError("Failed CryptDestroyKey/n");

if(hCryptProv)
    if (!CryptReleaseContext(hCryptProv, 0))
        MyHandleError("Failed CryptReleaseContext/n");

printf("/nThe program ran to completion without error. /n");

} // End of main.
当我们已经生成了一个密钥,我们下面要做的是如何保存,导出,导入一个对话密钥。将一个密钥保存起来,导出,保存到硬盘上。这样,这个密钥就可以在其它应用程序上使用,而不会随着生成密钥程序的关闭而丢失了。
下面这段程序就是用来演示如何保存,导出一个对话密钥的。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);

void main(void)
{
//--------------------------------------------------------------------
// Declare and initialize variables.

HCRYPTPROV hProv;       // CSP handle
HCRYPTKEY hSignKey;     // Signature key pair handle
HCRYPTKEY hXchgKey;     // Exchange key pair handle
HCRYPTKEY hKey;         // Session key handle
BYTE *pbKeyBlob;        // Pointer to a simple key BLOB
DWORD dwBlobLen;        // The length of the key BLOB

//--------------------------------------------------------------------
// Acquire a cryptographic provider context handle.

if(CryptAcquireContext(                             获取一个缺省容器的CSP句柄
   &hProv,
   NULL,
   NULL,
   PROV_RSA_FULL,
   0))
{
    printf("The CSP has been acquired. /n");
}
else
{
    MyHandleError("Error during CryptAcquireContext.");
}

if(CryptGetUserKey(                                    获取一个AT_SIGNATURE类型的密钥句柄
   hProv,
   AT_SIGNATURE,
   &hSignKey))
{
    printf("The signature key has been acquired. /n");
}
else
{
    MyHandleError("Error during CryptGetUserKey for signkey.");
}
//--------------------------------------------------------------------

if(CryptGetUserKey(                                      获取一个AT_KEYEXCHANGE,类型的密钥句柄,保存在hXchgKey中
   hProv,
   AT_KEYEXCHANGE,
   &hXchgKey))
{
    printf("The key exchange key has been acquired. /n");
}
else
{
    printf("Error during CryptGetUserKey exchange key.");
}
// Generate a session key.

if (CryptGenKey(                                  生成一个CRYPT_EXPORTABLE(可导出的),CALG_RC4(指定算法)的密钥,保存在hKey
    hProv,     
    CALG_RC4,     
    CRYPT_EXPORTABLE,
    &hKey))
{  
    printf("Original session key is created. /n");
}
else
{
   MyHandleError("ERROR -- CryptGenKey.");
}
if(CryptExportKey(                                   CryptExportKey导出一个密钥
   hKey,                                          将要导出的密钥的句柄
   hXchgKey,                   用户最终使用到的密钥的句柄
   SIMPLEBLOB,                                指定BLOB的类型,SIMPLEBLOB说明是 用来导出对话密钥的
   0,                                             指定密钥的附加属性
   NULL,                                        
   &dwBlobLen))                                  当时这个函数在这里的主要
                        目的是得到这个BLOB的长度
{
     printf("Size of the BLOB for the session key determined. /n");
}
else
{
     MyHandleError("Error computing BLOB length.");
}

if(pbKeyBlob = (BYTE*)malloc(dwBlobLen))
{
    printf("Memory has been allocated for the BLOB. /n");
}
else
{
    MyHandleError("Out of memory. /n");
}
if(CryptExportKey(                            这是这个函数才是真正的导出密钥
   hKey,
   hXchgKey,
   SIMPLEBLOB,
   0,
   pbKeyBlob,
   &dwBlobLen))
{
     printf("Contents have been written to the BLOB. /n");
}
else
{
    MyHandleError("Error during CryptExportKey.");
}
free(pbKeyBlob);                                  释放内存

// Destroy the session key.
if(hKey)
    CryptDestroyKey(hKey);

// Destroy the signature key handle.
if(hSignKey)
    CryptDestroyKey(hSignKey);

// Destroy the key exchange key handle.
if(hXchgKey)
     CryptDestroyKey(hXchgKey);

// Release the provider handle.
if(hProv)
   CryptReleaseContext(hProv, 0);
printf("The program ran to completion without error. /n");

}

 

 

 

原创粉丝点击