David.Turing's blog

 

CryptoAPI第一天

[转]http://www.chinaitpower.com/A200507/2005-07-27/175804.html

一:准备工作
一般必须包含如下头
文件 以及定义
#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 = ' ';