??xml version="1.0" encoding="utf-8" standalone="yes"?>
一Q准备工?br />一般必d含如下头
文g
以及定义
#include
#include
#include
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
当包含wincrypt.h?/font>
文g
Ӟ一般都需要定?define _WIN32_WINNT 0xQ具体的|Q否则将得到如下错误Qerror C2065: undeclared identifierQ不同的操作
pȝ
不同定义如下Q?br />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的配|信息存储在注册表中Q包括如下密钥:
HKEYQLOCALQMACHINEQSOFTWAREQMicrosoft \ Cryptography \Defaults
HKEY_CURRENT_USER\ Software \ Microsoft\ Cryptography \Providers
---- CryptoAPI使用两种密钥Q会话密钥与公共/Uh密钥寏V会话密钥用相同的加密和解密密钥,q种法较快Q但必须保证密钥的安全传递。公?Uh密钥对用一个公共密钥和一个私人密钥,Uh密钥只有专h才能使用Q公共密钥可以广泛传播。如果密钥对中的一个用于加密,另一个一定用于解密。公?Uh密钥对算法很慢,一般只用于加密批数据Q例如用于加密会话密钥。CryptoAPI支持两种基本的编码方法:式~码和块~码。流式编码在明码文本的每一位上创徏~码位,速度较快Q但安全性较低。块~码在一个完整的块上Q一般ؓ64位)上工作,需要用填充的Ҏ(gu)对要~码的数据进行舍入,以组成多个完整的块。这U算法速度较慢Q但更安全?
三:下面q入具体的编E?
一Q?Creating a Key Container and Generating Keys
创徏一个密钥容器,在进行加密,解密
文g
Qƈ且签名的时候,必须需要一个公/U钥对,下面我们来创徏默认的密钥容器,要注意的是创建密钥容器ƈ不会自动产生?U钥?
下面是我?/font>
E序
的Q务:
1Q假如密钥容器不存在则创Z个?br /> 2Q假如签名密钥不存在则在密钥容器里创Z个?br /> 3Q假如交换密钥不存在则在密钥容器里创Z个?br /> 4Q获取CSP中的一些参?br /> 下面是具体的步骤Q?br /> 1Q连接缺省的CSP
BOOL WINAPI CryptAcquireContext(
HCRYPTPROV* phProv, //out
LPCTSTR pszContainer, //in
LPCTSTR pszProvider, //in
DWORD dwProvType, //in
DWORD dwFlags //in
);
W一个参数是q回的CSP句柄Q第二个是密钥容器的名字Q第三个是A null-terminated string that specifies the name of the CSP to be used.W四个是指定提供的类型。例如:CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))Q?br />如果当前机器的未曾设|过~省的密钥容器,因此必须为机器创建缺省的密钥容器?
CryptAcquireContext( &hCryptProv, UserName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)大家有没有看刎ͼ只是最后一个参C同而已Q多了一个CRYPT_NEWKEYSET而已?br /> 2Q取得CSP的参?br />BOOL WINAPI CryptGetProvParam(
HCRYPTPROV hProv,
DWORD dwParam,
BYTE* pbData,
DWORD* pdwDataLen,
DWORD dwFlags
);
W一个参数是CSP的句柄,W二个参数是需要取得的具体参数对象Q类型比较多Q具体请看MSDNQ?br />例子QCryptGetProvParam(hCryptProv, PP_CONTAINER, (BYTE *)szUserName, &dwUserNameLen, 0)
3Q函数返回所获取密钥cd的句柄(0表失败,?表成功)
BOOL WINAPI CryptGetUserKey(
HCRYPTPROV hProv,
DWORD dwKeySpec,
HCRYPTKEY* phUserKey
);
参数比较单,只谈谈第二次参数Q它可以是AT_KEYEXCHANGEQ交换密钥) or AT_SIGNATUREQ签名密钥)Q例如:
CryptGetUserKey(hCryptProv,AT_KEYEXCHANGE,&hKey)
4Q生一个随机的交换密钥或者公/U钥?br />BOOL WINAPI CryptGenKey(
HCRYPTPROV hProv,
ALG_ID Algid,
DWORD dwFlags,
HCRYPTKEY* phKey
)Q?br />ALG_ID 表明产生U钥所使用的算法。有如下参数Q?br />
微Y
提供的基本算?br />CALG_MD2QCALG_MD5QCALG_SHAQCALG_SHA1QCALG_MACQCALG_HMACQCALG_SSL3_SHAMD5QCALG_MD2QCALG_MD2
CALG_RSA_SIGNQCALG_RSA_KEYXQCALG_RC2QCALG_RC4QCALG_DES
微Y
提供的增强型法Q?br />CALG_MD2QCALG_MD5QCALG_SHAQCALG_SHA1
CALG_MACQCALG_HMAC QCALG_SSL3_SHAMD5QCALG_RSA_SIGNQCALG_RSA_KEYXQCALG_RC2QCALG_RC4QCALG_DESQCALG_3DES_112QCALG_3DES
使用DH的CSP有如下两个参敎ͼCALG_DH_EPHEMQCALG_DH_SF
使用公开密钥法QAT_KEYEXCHANGEQAT_SIGNATURE
dwFlags,Q表C密钥用的长度Q参数可以ؓ0Q采用默认的密钥长度。或者是q行如下几个参数的或Q?br />CRYPT_ARCHIVABLEQ表C在句柄在关闭之前都能够被导?br />CRYPT_CREATE_SALTQ表C密钥按照一个salt value来随Z生?br />CRYPT_EXPORTABLEQ表C密钥可以从CSP中导出到BLOBQ因Z话密钥生是必须可导出的Q所以必设|?br />CRYPT_NO_SALTQ表C没有SALT VALUE获取allocated for a forty-bit symmetric key
CRYPT_PREGENQ表C在DH或者DSS密钥产生必须有个初始化?br />例如QCryptGenKey(hCryptProv,AT_KEYEXCHANGE,0,&hKey)
5Q释放CSP句柄
BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags //保留字,现在必须?
);
6QؓCSP增加一个reference countQ用来跟tCOM对象的整数|当对象创建,gؓ1。每ơ对对象的操作都增加,而对对象的关闭将减少Q当gؓ0是,对象释放Q所以与对象相关操作无效)
BOOL WINAPI CryptContextAddRef(
HCRYPTPROV hProv,
DWORD* pdwReserved, //保留字,必须为NULL
DWORD dwFlags //保留字,必须?
);
二:Deriving a Session Key from a Password
1Q连接CSP
2Q用CryptCreateHash产生一个空的HASH对象
3Q对密码q行HASH处理
4Q释放HASH以及密码对象
5Q释放CSP
下面是具体的步骤Q?br /> 1QCryptCreateHash初始化一个HASH对象
BOOL WINAPI CryptCreateHash(
HCRYPTPROV hProv, //in
ALG_ID Algid, //in
HCRYPTKEY hKey, //in
DWORD dwFlags, //in保留字,必须?
HCRYPTHASH* phHash //out
);
W二个参数是指定HASH法Q有CALG_HMAC,CALG_MAC,CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_SSL3_SHAMD5。第三个参数对于那些keyed hashQ例如HMACQMAC法。但是nonkeyed法Q必设|ؓ0?br /> 2QCryptHashDataҎ(gu)据用HASH
BOOL WINAPI CryptHashData(
HCRYPTHASH hHash, //in,HASH对象句柄
BYTE* pbData, //in,待HASH的数?br /> DWORD dwDataLen, //in,待HASH数据的长度,当dwFlags为CRYPT_USERDATA?Ӟ必须?
DWORD dwFlags //in,一般ؓ0Q或者ؓCRYPT_USERDATAQ用在用戯?/font>
pȝ
旉要输入PINQ?br />);
3QCryptDeriveKey从某一数据产生会话密钥Q他有点cMCryptGenKeyQ但是他产生的会话密钥来自固定数据,而CryptGenKey是随Z生的。ƈ且不能生公/U钥?br />BOOL WINAPI CryptDeriveKey(
HCRYPTPROV hProv, //in,CSP句柄
ALG_ID Algid, //in,指定的算法,cMCryptGenKey
HCRYPTHASH hBaseData, //in,HASH对象的句?br /> DWORD dwFlags, //in,指定产生密钥的类?br /> HCRYPTKEY* phKey //in,out产生的密钥句柄地址
);
例如QCryptDeriveKey(hCryptProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)
4QCryptDestroyHash(hHash);
5, CryptDestroyKey(hKey);
6, 在这里发C个不错的函数Q就是那U提C入密码的命o行(屏幕只会出现***Q?br /> 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,q接CSP
2,使用CryptGenKey产生一个会话密?br /> 3,CryptDuplicateKey复制会话密钥
4,CryptSetKeyParam改变密钥产生的过E?br /> 5,CryptGenRandom产生随机?br />具体q程?br />1QCryptDuplicateKe复制会话密钥
BOOL WINAPI CryptDuplicateKey(
HCRYPTKEY hKey, //in 会话密钥句柄
DWORD* pdwReserved, //in 保留字,必须为NULL
DWORD dwFlags, //in 保留字,必须?
HCRYPTKEY* phKey //out 新的会话密钥
);
2,CryptSetKeyParam定制会话密钥的参?br />BOOL WINAPI CryptSetKeyParam(
HCRYPTKEY hKey,
DWORD dwParam, //in 很多Q具体请看MSDN
BYTE* pbData,
DWORD dwFlags //in 只有在dwParam=KP_ALGID才被使用
);
例如CryptSetKeyParam(hOriginalKey, KP_MODE, (BYTE*)&dwMode, 0)(dwMode = CRYPT_MODE_ECB)
3,CryptGenRandom为空间生随机字?br />BOOL WINAPI CryptGenRandom(
HCRYPTPROV hProv,
DWORD dwLen, //需要生的随机比特?br /> BYTE* pbBuffer //需要返回数据的I间Q这个pbBuffer可以{于CryptSetKeyParam的pbData
);
例如QCryptGenRandom(hCryptProv, 8, pbData)
4,CryptGetKeyParam获取密钥的一些参?br />BOOL WINAPI CryptGetKeyParam(
HCRYPTKEY hKey,
DWORD dwParam, //in,参数众多
BYTE* pbData, //outQ获取BYTE数据的指?br /> DWORD* pdwDataLen, //outQ获取BYTE数据的长?br /> DWORD dwFlags //关键字,必须?
);
例如QCryptGetKeyParam(hKey, KP_IV, pbData, &dwCount, 0)
四:Exporting a Session Key
1,q接CSP
2,CryptGetUserKey获取?U钥对和交换密钥Q公U钥用来{Q而交换密钥用来导Z话密?br /> 3,CryptGenKey产生会话密钥
4,CryptExportKey创徏单包含有会话密钥的key BLOB
5,释放处理Q?br /> 具体q程Q?br />1,CryptExportKey函数导出密钥
BOOL WINAPI CryptExportKey(
HCRYPTKEY hKey, //需要导出的密钥句柄
HCRYPTKEY hExpKey, //待导出密钥用交换密钥进行加密,假如是公开的BLOG当然p|ؓ0
DWORD dwBlobType, //指定导出的密钥BLOBcd。六个参数见MSDN
DWORD dwFlags, //CRYPT_DESTROYKEYQCRYPT_SSL2_FALLBACKQCRYPT_OAEP
BYTE* pbData, //导出的数据指针,以后可以将q个数据写如盘或者别的Q务?br /> DWORD* pdwDataLen //导出的数据长?br />);
例如QCryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen)
2QCryptImportKey密钥从BLOB转换到CSP?br />BOOL WINAPI CryptImportKey(
HCRYPTPROV hProv, //CSP句柄
BYTE* pbData, //待{换的BLOB数据
DWORD dwDataLen, //待{换的数据长度
HCRYPTKEY hPubKey, //对BLOB解密的公钥,譬如上面是用交换密钥密钥加密的,q交换密钥解密
DWORD dwFlags, //目前q只应用在当一对公/U钥从PRIVATEKEYBLOB中加入CSP中这U情c?br /> HCRYPTKEY* phKey //out导入的密?br />);
例如QCryptImportKey(hProv,pbKeyBlob,dwBlobLen,0,0,&hPubKey)
五:Encoding and Decoding Messages
~码的处理过E?br /> 1Q将待编码的数据转化为合适的格式Q?br /> 2Q调用CryptMsgOpenToEncodeQpassing the necessary argument;
3, 调用CryptMsgUpdate函数多次Q最后一ơ调用时Q将final参数讄为true
4, 调用CryptMsgGetParam来获取一个需要得到的参数?br /> 5, 调用CryptMsgClose来关闭消?br /> 解码的处理过E?br /> 1Q检查申L攄码后数据的空_利用函数CryptMsgCalculateEncodedLength.
2Q调用函数CryptMsgOpenToDecode,passing the necessary argumentQ?br /> 3Q调用CryptMsgUpdate一ơ,q将D合适的动作d理信息,以来于信息的格式
4Q一些额外的处理Q例如额外的解密或者是验证Q调用CryptMsgControl,
5Q调用CryptMsgGetParam来获取需要得到的参数
6Q调用CryptMsgClose来关闭消?br /> 具体的函Cl:
1QCryptMsgCalculateEncodedLength计算所需要的存储~码的最大空间?br />DWORD WINAPI CryptMsgCalculateEncodedLength(
DWORD dwMsgEncodingType,//指定~码cd。一般ؓX509_ASN_ENCODING|PKCS_7_ASN_ENCODING
DWORD dwFlags,
DWORD dwMsgType,
const void* pvMsgEncodeInfo, //in 指向待编码的数据Q数据类型依赖于dwMsgType
LPSTR pszInnerContentObjID,
DWORD cbData //in 比特数的定w
);
W二个参敎ͼCMSG_BARE_CONTENT_FLAGQCMSG_DETACHED_FLAGQCMSG_CONTENTS_OCTETS_FLAGQCMSG_CMS_ENCAPSULATED_CONTENT_FLAG
W三个参敎ͼCMSG_DATA,CMSG_SIGNED,CMSG_ENVELOPED,CMSG_SIGNED_AND_ENVELOPED,CMSG_HASHED,CMSG_ENCRYPTED
W五个参敎ͼszOID_RSA_data,szOID_RSA_signedData,szOID_RSA_envelopedData,szOID_RSA_signEnvData,szOID_RSA,digestedData ,
szOID_RSA_encryptedData,SPC_INDIRECT_DATA_OBJID,NULL
q回|q回需要的一个加密信息所需要的长度
2QCryptMsgOpenToEncode打开一个消息以便进行编码,q回打开消息的句?br /> HCRYPTMSG WINAPI CryptMsgOpenToEncode(
DWORD dwMsgEncodingType, //指定~码cd。一般ؓX509_ASN_ENCODING|PKCS_7_ASN_ENCODING
DWORD dwFlags,
DWORD dwMsgType,
const void* pvMsgEncodeInfo,
LPSTR pszInnerContentObjID, //和CryptMsgCalculateEncodedLength一?br /> PCMSG_STREAM_INFO pStreamInfo //当流没被使用Ӟ该参CؓNULL
);
W二个参敎ͼCMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG,
CMSG_CRYPT_RELEASE_CONTEXT_FLAG
W三个参敎ͼ
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
3QCryptMsgOpenToDecode打开一个消息以便进行解码,q回打开消息的句?br /> CRYPTMSG WINAPI CryptMsgOpenToDecode(
DWORD dwMsgEncodingType, //指定~码cd。一般ؓX509_ASN_ENCODING|PKCS_7_ASN_ENCODING
DWORD dwFlags, //CMSG_DETACHED_FLAGQCMSG_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
);
4QCryptMsgUpdate增加内容到加密信息中
BOOL WINAPI CryptMsgUpdate(
HCRYPTMSG hCryptMsg, //待更新的加密信息句柄
const BYTE* pbData, //待编?解码的数?br /> DWORD cbData, // pbData 的数据长?br /> BOOL fFinal
);
W四个参敎ͼ当CMSG_DETACHED_FLAG没有讄Qƈ且信息由CryptMsgOpenToDecode或CryptMsgOpenToEncode打开Q那么fFinal被设|ؓTRUEQƈ且CryptMsgUpdate只被调用一ơ。当CMSG_DETACHED_FLAG被设|,q且信息?CryptMsgOpenToEncode打开Q那么仅在最后一ơ调用CryptMsgUpdate才被讄为TRUE。当CMSG_DETACHED_FLAG被设|,q且信息由CryptMsgOpenToDecode打开Q那么仅在信息头单独被处理时CryptMsgUpdate才被讄为TRUE?br /> 5QCryptMsgGetParam在数据编?解码后获取参?br /> BOOL WINAPI CryptMsgGetParam(
HCRYPTMSG hCryptMsg, //in 信息句柄
DWORD dwParamType, //in 参数众多Q参见MSDN
DWORD dwIndex, //in 可适用的返回参数句?假如参数没有被获取,则被忽略或则?
void* pvData, //out 获取的数据指?br /> DWORD* pcbData //in,out数据长度
);
6QCryptMsgClose关闭信息句柄
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.