Windows CryptoAPI入门

CryptoAPI为开发者提供在Windows下使用PKI的API。CryptoAPI包括编码、解码、加密、解密、Hash、数字证书管理和证书存储等功能。

常用API

CSP相关

一个CSP是实现加密操作的独立模块,要实现加密,至少需要一个CSP。每个CSP对CryptAPI的实现是不同的,使用的算法不同,有些包含了对硬件的支持。

  • CryptAcquireContext 获得指定CSP的密钥容器的句柄
  • CryptReleaseContext 释放由CryptAcquireContext得到的句柄

密钥相关

用来创建和销毁密钥,也可以使用一个已有的密钥

  • CryptDeriveKey 从一个密码中派生一个密钥
  • CryptDestoryKey 销毁密钥
  • CryptDuplicateKey 制作一个密钥和密钥状态的精确复制
  • CryptGenKey 创建一个随机密钥
  • CryptImportKey 把一个密钥BLOB传送到CSP中

加解密相关

用来加解密数据,需要指定一个密钥,这个密钥可以是由CryptGenKeyCryptDuplicateKeyCryptImportKey产生。

  • CryptEncrypt 使用指定加密密钥来加密一段信息
  • CryptDecrypt 使用指定加密密钥来解密一段密文

哈希和数字签名函数

用来计算Hash、创建和校验数字签名。

  • CryptCreateHash 创建一个空哈希对象
  • CryptDestoryHash 销毁一个哈希对象
  • CryptDuplicateHash 复制一个哈希对象
  • CryptGetHashParam 得到一个哈希对象参数
  • CryptHashData 对一块数据进行哈希,把它加到指定的哈希对象中
  • CryptHashSessionKey 对一个会话密钥进行哈希,把它加到指定的哈希对象中
  • CryptSetHashParam 设置一个哈希对象的参数
  • CryptSignHash 对一个哈希对象进行签名
  • CryptVerifySignature 校验一个数字签名

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <windows.h>
#include <wincrypt.h>
#pragma comment (lib, "advapi32")
int main()
{
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;

CryptAcquireContext(&hCryptProv, // CSP句柄的指针
NULL, // 密钥容器名称
MS_DEF_PROV, // CSP名称(如果为NULL,就使用默认的CSP)
PROV_RSA_FULL, // CSP类型
CRYPT_VERIFYCONTEXT); // 此选项指出应用程序不需要使用公钥/私钥对

CryptCreateHash(hCryptProv, // CSP句柄
CALG_MD5, // Hash算法的标识符
0, // 如果哈希算法是密钥哈希,如HMAC或MAC算法,就用此密钥句柄传递密钥
0, // 保留,必须为0
&hCryptHash); // 哈希对象的句柄指针

static char szPassword[] = "FSD@#fds23"; // 加密密码
DWORD dwLen = strlen(szPassword);

// 计算密码的Hash值
CryptHashData(hCryptHash,
(BYTE*)szPassword,
dwLen,
0);

// 根据密码的Hash值派生一个密钥
CryptDeriveKey(hCryptProv, // CSP句柄
CALG_RC4, // 要产生密钥的对称加密算法
hCryptHash, // 哈希对象的句柄
0, // 密钥类型
&hCryptKey); // 输出密钥句柄指针

// 待加密数据
static char szData[] = "important data";

DWORD dwLenIn = strlen(szData);
DWORD dwLenOut = dwLenIn;

CryptEncrypt(hCryptKey, // 密钥句柄
0, // 如果在加密的同时需要计算Hash值,传入一个Hash对象句柄
TRUE, // 标识是否是最后一次加密操作
0, // 保留,必须为0
(BYTE*)szData, // 要被加密的数据
&dwLenOut, // [in/out] 传入需要加密的数据长度,传出已经加密的数据长度
dwLenIn); // 指出szData的Buffer长度

CryptDecrypt(hCryptKey, // CSP句柄
0, // 如果在加密的同时需要计算Hash值,传入一个Hash对象句柄
TRUE, // 标识是否是最后一个解密操作
0, // 保留,必须为0
(BYTE*)szData, // [in/out] 需要解密数据的地址
&dwLenOut); // [in/out] 传入需要解密的数据长度,传出已经解密的数据长度

// 清理工作
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, NULL);
}

实际应用时

  • 应该加上每个步骤成功与否的检查、错误处理。
  • 当要加解密的数据很长时,通常会把数据分多个块进行加解密,此时就需要计算是否是最后一个块,改变传入的参数。
  • 如果要使用文件来保存、处理加解密后的数据,可以使用Base64编码,使得数据全部都是可见字符,以防止文件写入时遇到某些不可见字符被截断。
给鸡排饭加个蛋