WindowsAPI编程核心技术-加密技术

CryptoAPI

入门

CryptAcquireContext

获取特定CSP内特定密钥容器句柄。

1
2
3
4
5
6
7
8
9
10
11
12
BOOL WINAPI CryptAcquireContext(
_Out_ HCRYPTPROV *phProv,
//CSP句柄指针
_In_ LPCTSTR pszContainer,
//密钥容器名称
_In_ LPCTSTR pszProvider,
//CSP名称
_In_ DWORD dwProvType,
//获取提供程序的类型
_In_ DWORD dwFlags
//标志
)//成功TRUE 失败FALSE

dwProvType可以是:

CSP类型 密钥交换算法 签名算法 对称加密算法 Hash算法
PROV_RSA_FULL RSA RSA RC2、RC4 MD5、SHA
PROV_RSA_SIG RSA MD5、SHA
PROV_RSA_SCHANNEL RSA RSA RC4、DES、3DES MD5、SHA
PROV_DSS DSS DSS MD5、SHA
PROV_DSS_DH DH DSS CYLINK_MEK MD5、SHA
PROV_DH_SCHANNEL DH DSS DES、3DES MD5、SHA
PROV_FORTEZZA KEA DSS Skipjack SHA
PROV_MS_EXCAHNGE RSA RSA CAST MD5
PROV_SSL RSA RSA Varies Varies

dwFlags参数可以是:

含义
CRYPT_VERIFYCONTEXT 不需要公私钥对,只哈希和对称加密
CRYPT_NEWKEYSET 创建新密钥容器,pszContainer位NULL则默认名称
CRYPT_MACHINE_KEYSET 创建的密钥容器只能由创建者本人或系统管理员使用
CRYPT_DELETEKEYSET 删除pszContainer容器,容器中密钥对也被删除
CRYPT_SILENT CSP不显示用户界面

CryptEnumProviders

枚举所有CSP,需循环调用。

1
2
3
4
5
6
7
8
BOOL WINAPI CryptEnumProviders(
DWORD dwIndex, //枚举下一个CSP索引
DWORD* pdwReserved, //必须NULL
DWORD dwFlags, //必须0
DWORD* pdwProvType, //CSP类型
LPTSTR pszProvName, //CSP名称缓冲区 可NULL得到字符串大小
DWORD* pcbProvName //缓冲区字符串长度
); //成功TRUE 否则FALSE

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <WinCrypt.h>
int main() {
DWORD dwIndex = 0;
DWORD dwType;
DWORD cbNameLen;
LPWSTR pszName;
DWORD i = 0;
//查看机器中所有的CSP
pszName = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, 256);
cbNameLen = 256;
while (CryptEnumProviders(dwIndex++, NULL, 0, &dwType, pszName, &cbNameLen)) {
wprintf(L"dwType=%2.0d,len=%d CSPName=%s\n", dwType, cbNameLen, pszName);
cbNameLen = 256; //这里要加这个
}
LocalFree(pszName);
return 0;
}

CryptGetDefaultProvider

获取系统默认CSP:

1
2
3
4
5
6
7
BOOL CryptGetDefaultProvider(
DWORD dwProvType, //默认CSP类型
DWORD* pdwReserved, //NULL
DWORD dwFlags, //标志位
LPSTR pszProvName, //CSP名称缓冲区
DWORD* pcbProvName //输入时缓冲区大小 输出时CSP名称大小
); //成功TRUE 否则FALSE

CryptSetProvider

指定当前用户默认加密服务提供程序:

1
2
3
4
BOOL CryptSetProvider(
LPCTSTR pszProvName, //CSP名称缓冲区
DWORD dwProvType //CSP类型
); //成功TRUE 否则FALSE

CryptGetProvParam

获取CSP各种参数属性:

1
2
3
4
5
6
7
BOOL CryptGetProvParam(
HCRYPTPROV hProv, //CSP句柄
DWORD dwParam, //查询参数
BYTE* pbData, //接收缓冲区
DWORD* pdwDataLen, //缓冲区数据长度
DWORD dwFlags //当dwParam为PP_ENUMCONTAINERS时这里用CRYPT_MACHINE_KEYSET
); //成功TRUE 否则FALSE

dwParam可以是:

参数 作用
PP_CONTAINER 密钥名称
PP_ENUMALGS CSP支持的算法
PP_ENUMALGS_EX 同上,获得更多信息
PP_ENUMCONTAINERS CSP支持的密钥容器
PP_IMPTYPE CSP怎样实现
PP_NAME CSP名称
PP_VERSION CSP版本号
PP_KEYSIZE_INC AT_SIGNATURE位数
PP_KEYX_KEYSIZE_INC AT_KEYEXCHANGE位数
PP_KEYSET_SEC_DESCR 密钥安全描述符
PP_UNIQUE_CONTAINER 当前容器密钥唯一名称
PP_PROVTYPE CSP类型

CryptSetProvParam

设置CSP各种参数:

1
2
3
4
5
6
BOOL CryptSetProvParam(
HCRYPTPROV hProv, //CSP句柄
DWORD dwParam, //参数
BYTE* pdData, //设置数据缓冲区
DWORD dwFlags //标志位
); //成功TRUE 否则FALSE

dwParam可选值为:

含义
PP_CLIENT_HWND 设置Windows句柄
PP_KEYSET_SEC_DESCR 密钥安全描述
PP_USE_HARDWARE_RNG 硬件是否支持随机数发生器

CryptReleaseContext

断开CSP并释放CSP句柄:

1
2
3
4
BOOL CryptReleaseContext(
HCRYPTPROV hProv, //CSP句柄
DWORD dwFlags //0
); //成功TRUE 否则FALSE

HASH

CryptCreateHash

创建空HASH对象。

1
2
3
4
5
6
7
8
9
10
11
12
BOOL WINAPI CryptCreateHash(
_In_ HCRYPTPROV hProv,
//CSP句柄
_In_ ALG_ID Algid,
//要使用的HASH算法ID CALG_MD5 CALG_SHA1 CALG_SHA256等
_In_ HCRYPTKEY hKey,
//对于非键控算法必须为0
_In_ DWORD dwFlags,
//通常0
_Out_ HCRYPTHASH *phHash
//新HASH对象地址
)//成功TRUE 失败FALSE

CryptHashData

数据添加到HASH对象并计算。

1
2
3
4
5
6
7
8
9
10
BOOL WINAPI CryptHashData(
_In_ HCRYPTHASH hHash,
//HASH对象句柄
_In_ BYTE *pbData,
//要添加的数据的缓冲区指针
_In_ DWORD dwDataLen,
//大小
_In_ DWORD dwFlags
//通常0
)//成功TRUE 失败FALSE

CryptGetHashParam

从HASH对象中获取指定参数值。

1
2
3
4
5
6
7
8
9
10
11
12
BOOL WINAPI CryptGetHashParam(
_In_ HCRYPTHASH hHash,
//HASH对象句柄
_In_ DWORD dwParam,
//查询类型 HP_ALGID算法 HP_HASHSIZE结果字节数 HP_HASHVAL结果
_Out_ BYTE *pbData,
//接收指定值的数据缓冲区指针
_Inout_ DWORD *pdwDataLen,
//指向pbData大小的指针
_In_ DWORD dwFlags
//保留0
)//成功TRUE 失败FALSE

例子

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
#include <windows.h>
#include <wincrypt.h> //必须在windows.h后
#include <cstdio>
BOOL CalculateHash(PBYTE pData, DWORD dwDataLength, ALG_ID algHashType, PBYTE* ppHashData, PDWORD pdwHashDataLength) {
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hCryptHash = NULL;
DWORD dwHashDataLength = 0, dwTemp = 0;
BYTE* pHashData = NULL;
BOOL bRet = FALSE;
do {
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
if (bRet == FALSE)
break;
bRet = ::CryptCreateHash(hCryptProv, algHashType, NULL, NULL, &hCryptHash);
if (FALSE == bRet)
break;
bRet = ::CryptHashData(hCryptHash, pData, dwDataLength, 0);
if (FALSE == bRet)
break;
dwTemp = sizeof(dwHashDataLength);
bRet = ::CryptGetHashParam(hCryptHash, HP_HASHSIZE, (BYTE*)(&dwHashDataLength), &dwTemp, 0);
if (FALSE == bRet)
break;
pHashData = new BYTE[dwHashDataLength];
if (NULL == pHashData) {
bRet = FALSE;
break;
};
::RtlZeroMemory(pHashData, dwHashDataLength);
bRet = ::CryptGetHashParam(hCryptHash, HP_HASHVAL, pHashData, &dwHashDataLength, 0);
if (FALSE == bRet)
break;
} while (FALSE);
if (bRet == TRUE) {
for (size_t nIdx = 0; nIdx < dwHashDataLength; nIdx++)
printf("%02X", pHashData[nIdx]);
putchar('\n');
};
if (NULL != pHashData) {
delete[]pHashData;
pHashData = NULL;
};
if (NULL != hCryptProv) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (NULL != hCryptHash) {
::CryptDestroyHash(hCryptHash);
hCryptHash = NULL;
};
return bRet;
};

AES

CryptDeriveKey

从基础数据值派生出加密会话密钥。

1
2
3
4
5
6
7
8
9
10
11
12
BOOL WINAPI CryptDeriveKey(
_In_ HCRYPTPROV hProv,
//CSP句柄
_In_ ALG_ID Algid,
//要为其生成密钥对称加密算法的ID CALG_AES_128默认 CALG_DES
_In_ HCRYPTHASH hBaseData,
//基础数据
_In_ DWORD dwFlags,
//生成密钥类型 有枚举值
_Inout_ HCRYPTKEY* phKey
//接收新生成密钥句柄地址
)//成功TRUE 失败FALSE

CryptEntrypt

加密数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL WINAPI CryptEncrypt(
_In_ HCRYPTKEY hKey,
//加密密钥
_In_ HCRYPTHASH hHash,
//HASH对象 不HASH为NULL
_In_ BOOL Final,
//是否为最后一个块
_In_ DWORD dwFlags,
//保留0
_Inout_ BYTE* pbData,
//要加密的明文缓冲区指针 接收密文
_Inout_ DWORD* pdwDataLen,
//读缓冲区中明文大小 写密文大小
_In_ DWORD dwBufLen
//明文缓冲区总大小
)//成功TRUE 失败FALSE

CryptDecrypt

解密数据。

1
2
3
4
5
6
7
8
BOOL WINAPI CryptDecrypt( //都同上 懒得写了
_In_ HCRYPTKEY hKey,
_In_ HCRYPTHASH hHash,
_In_ BOOL Final,
_In_ DWORD dwFlags,
_Inout_ BYTE* pbData,
_Inout_ DWORD* pdwDataLen
)

例子

不直接拿明文密钥加密,先把密钥进行MD5。AES一个组只能$128$位,密钥长度可以为$128$位、$192$位或$256$位。

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <cstdio>
BOOL FileWriteBack(const char* pcszFilePathName, PBYTE pbData, SIZE_T nBufferSize) {
BOOL bRet = FALSE;
FILE* fp = NULL;
fp = fopen(pcszFilePathName, "wb");
if (NULL == fp)
return bRet;
fwrite(pbData, nBufferSize, 1, fp);
if (NULL != fp) {
fclose(fp);
fp = NULL;
};
return bRet;
};
BOOL ReadFromFile(LPCSTR lpcszFilePathName, PBYTE* ppData, DWORD& dwFileSize, size_t& nFileBufferLength) {
BOOL bRet = FALSE;
FILE* fp = NULL;
LONG lSize = 0;
PBYTE pbData = NULL;
fp = fopen(lpcszFilePathName, "rb");
if (NULL == fp)
return FALSE;
fseek(fp, 0, SEEK_END);
lSize = ftell(fp);
pbData = new BYTE[lSize * 16];
::RtlZeroMemory(pbData, lSize * 16);
dwFileSize = lSize;
nFileBufferLength = lSize * 16;
fseek(fp, 0, SEEK_SET);
fread(pbData, lSize, 1, fp);
if (NULL != fp) {
fclose(fp);
fp = NULL;
};
*ppData = pbData;
return TRUE;
}
BOOL AesEncrypt(const PBYTE pcszPassword, SIZE_T nPasswordLength,LPCSTR lpcszOrigFilePathName,LPCSTR lpcszEncFilePathName) {
BOOL bRet = FALSE;
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hCryptHash = NULL;
HCRYPTKEY hCryptKey = NULL;
PBYTE pbData = NULL;
DWORD dwDataLength = 0;
SIZE_T nBufferLength=0;
do {
bRet = ReadFromFile(lpcszOrigFilePathName, &pbData, dwDataLength, nBufferLength);
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
if (FALSE == bRet)
break;
bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
if (FALSE == bRet)
break;
bRet = ::CryptHashData(hCryptHash, pcszPassword, nPasswordLength, 0);
if (FALSE == bRet)
break;
bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
if (FALSE == bRet)
break;
bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pbData, &dwDataLength, nBufferLength);
if (FALSE == bRet)
break;
bRet = FileWriteBack(lpcszEncFilePathName, pbData, dwDataLength);
} while (FALSE);
if (NULL != hCryptProv) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (NULL != hCryptHash) {
::CryptDestroyHash(hCryptHash);
hCryptHash = NULL;
};
if (NULL != hCryptKey) {
::CryptDestroyKey(hCryptKey);
hCryptKey = NULL;
};
if (NULL != pbData) {
delete[]pbData;
pbData = NULL;
};
return bRet;
};
BOOL AesDecrypt(const PBYTE pcszPassword, SIZE_T nPasswordLength, LPCSTR lpcszOrigFilePathName, LPCSTR lpcszEncFilePathName) {
BOOL bRet = FALSE;
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hCryptHash = NULL;
HCRYPTKEY hCryptKey = NULL;
PBYTE pbData = NULL;
DWORD dwDataLength = 0;
SIZE_T nBufferLength = 0;
do {
bRet = ReadFromFile(lpcszOrigFilePathName, &pbData, dwDataLength, nBufferLength);
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
if (FALSE == bRet)
break;
bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
if (FALSE == bRet)
break;
bRet = ::CryptHashData(hCryptHash, pcszPassword, nPasswordLength, 0);
if (FALSE == bRet)
break;
bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
if (FALSE == bRet)
break;
bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pbData, &dwDataLength);
if (FALSE == bRet)
break;
bRet = FileWriteBack(lpcszEncFilePathName, pbData, dwDataLength);
if (FALSE == bRet)
break;
bRet = TRUE;
} while (FALSE);
if (NULL != hCryptProv) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (NULL != hCryptHash) {
::CryptDestroyHash(hCryptHash);
hCryptHash = NULL;
};
if (NULL != hCryptKey) {
::CryptDestroyKey(hCryptKey);
hCryptKey = NULL;
};
if (NULL != pbData) {
delete[]pbData;
pbData = NULL;
};
return bRet;
};

RSA

CryptGenKey

随机生成加密会话密钥或公钥/私钥对,返回句柄。

1
2
3
4
5
6
7
8
9
10
BOOL WINAPI CryptGenKey(
_In_ HCRYPTPROV hProv,
//CSP句柄
_In_ ALG_ID Algid,
//密钥算法
_In_ DWORD dwFlags,
//密钥类型
_Out_ HCRYPTKEY* phKey
//密钥或密钥对句柄
)//成功TRUE 失败FALSE

CryptExportKey

从CSP导出加密密钥或密钥对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOL WINAPI CryptExportKey(
_In_ HCRYPTKEY hKey,
//要导出的密钥句柄
_In_ HCRYPTKEY hExpKey,
//目标用户密钥句柄
_In_ DWORD dwBlobType,
//导出的类型
_In_ DWORD dwFlags,
//附加选项
_Out_ BYTE* pbData,
//接收数据缓冲区指针 NULL就塞到pdwDataLen
_Inout_ DWORD* pdwDataLen
//缓冲区大小
)//成功TRUE 失败FALSE

CryptImportKey

将密钥从密钥BLOB导入CSP中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOL WINAPI CryptImportKey(
_In_ HCRYPTPROV hProv,
//CSP句柄
_In_ BYTE* pbData,
//密钥BLOB
_In_ DWORD dwDataLen,
//BLOB长度
_In_ HCRYPTKEY hPubKey,
//?
_In_ DWORD dwFlags,
//?
_Out_ HCRYPTKEY* phKey
//接收导入键句柄HCRYPTKEY的指针
)//成功TRUE 失败FALSE

例子

导出公钥/私钥都要重复导两次,第一次只导出大小,动态申请足够内存后再导出密钥。

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
BOOL GenerateKey(PBYTE* ppPublicKey, PDWORD pdwPublicKeyLength, PBYTE* ppPrivateKey, PDWORD pdwPrivateKeyLength) {
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hCryptKey = NULL;
DWORD dwPublicKeyLength = 0, dwPrivateKeyLength = 0;
PBYTE pPublicKey = NULL, pPrivateKey = NULL;
BOOL bRet = FALSE;
do {
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
if (FALSE == bRet)
break;
bRet = ::CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hCryptKey);
if (FALSE == bRet)
break;
bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLength);
if (FALSE == bRet)
break;
pPublicKey = new BYTE[dwPublicKeyLength];
::RtlZeroMemory(pPublicKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength);
bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength);
if (FALSE == bRet)
break;
bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLength);
if (FALSE == bRet)
break;
pPrivateKey = new BYTE[dwPrivateKeyLength];
::RtlZeroMemory(pPrivateKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength);
bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength);
if (FALSE == bRet)
break;
bRet = TRUE;
*ppPublicKey = pPublicKey;
*pdwPublicKeyLength = dwPublicKeyLength;
*ppPrivateKey = pPrivateKey;
*pdwPrivateKeyLength = dwPrivateKeyLength;
} while (FALSE);
if (hCryptKey != NULL) {
::CryptDestroyKey(hCryptKey);
hCryptKey = NULL;
};
if (hCryptProv != NULL) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (pPublicKey != NULL) {
delete[]pPublicKey;
pPublicKey = NULL;
};
if (pPrivateKey != NULL) {
delete[]pPrivateKey;
pPrivateKey = NULL;
};
return bRet;
};
BOOL RsaEncrypt(PBYTE pPublicKey, DWORD dwPublicKeyLength, PBYTE pData, DWORD& dwDataLength, DWORD dwBufferLength) {
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hCryptKey = NULL;
BOOL bRet = FALSE;
do {
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
if (FALSE == bRet)
break;
bRet = ::CryptImportKey(hCryptProv, pPublicKey, dwPublicKeyLength, NULL, 0, &hCryptKey);
if (FALSE == bRet)
break;
bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
if (FALSE == bRet)
break;
} while (FALSE);
if (hCryptProv != NULL) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (hCryptKey != NULL) {
::CryptDestroyKey(hCryptKey);
hCryptKey = NULL;
};
return bRet;
};
BOOL RsaDecrypt(PBYTE pPrivateKey, DWORD dwPrivateKeyLength, PBYTE pData, DWORD& dwDataLength, DWORD dwBufferLength) {
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hCryptKey = NULL;
BOOL bRet = FALSE;
do {
bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
if (FALSE == bRet)
break;
bRet = ::CryptImportKey(hCryptProv, pPrivateKey, dwPrivateKeyLength, NULL, 0, &hCryptKey);
if (FALSE == bRet)
break;
bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
if (FALSE == bRet)
break;
} while (FALSE);
if (hCryptProv != NULL) {
::CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
};
if (hCryptKey != NULL) {
::CryptDestroyKey(hCryptKey);
hCryptKey = NULL;
};
return bRet;
};

CSP综合

例如加解密一个文件:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>
//确定使用RC2块编码或是RC4流式编码
#ifdef USE_BLOCK_CIPHER
#define ENCRYPT_ALGORITHM CALG_RC2
#define ENCRYPT_BLOCK_SIZE 8
#else
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 1
#endif
void CAPIDecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
void CAPIEncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
int main(int argc, char* argv[]) {
CHAR szSource[] = "d:\\plain.txt";
CHAR szDestination[] = "d:\\en.dat";
CHAR szDeDestination[] = "D:\\check.txt";
CHAR szPassword[] = "123";
CAPIEncryptFile(szSource, szDestination, szPassword);
CAPIDecryptFile(szDestination, szDeDestination, szPassword);
return 0;
}
/*szSource为要加密的文件名称,szDestination为加密过的文件名称,szPassword为加密口令*/
void CAPIEncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword) {
FILE* hSource = NULL;
FILE* hDestination = NULL;
INT eof = 0;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTKEY hXchgKey = 0;
HCRYPTHASH hHash = 0;
PBYTE pbKeyBlob = NULL;
DWORD dwKeyBlobLen;
PBYTE pbBuffer = NULL;
DWORD dwBlockLen = 16;
DWORD dwBufferLen;
DWORD dwCount;
hSource = fopen(szSource, "rb");// 打开源文件
if (!hSource) {
printf("fopen %s failed", szSource);
return;
}
hDestination = fopen(szDestination, "wb");//.打开目标文件
// 连接缺省的CSP
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);
if (szPassword == NULL) {
//口令为空,使用随机产生的会话密钥加密
// 产生随机会话密钥。
CryptGenKey(hProv, ENCRYPT_ALGORITHM, CRYPT_EXPORTABLE, &hKey);
// 取得密钥交换对的公共密钥
CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey);
// 计算隐码长度并分配缓冲区
CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, NULL, &dwKeyBlobLen);
pbKeyBlob = (PBYTE)malloc(dwKeyBlobLen);
// 将会话密钥输出至隐码
CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwKeyBlobLen);
// 释放密钥交换对的句柄
CryptDestroyKey(hXchgKey);
hXchgKey = 0;
// 将隐码长度写入目标文件
fwrite(&dwKeyBlobLen, sizeof(DWORD), 1, hDestination);
//将隐码长度写入目标文件
fwrite(pbKeyBlob, 1, dwKeyBlobLen, hDestination);
}
else {
//口令不为空, 使用从口令派生出的密钥加密文件
CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);// 建立散列表
CryptHashData(hHash, (BYTE*)szPassword, strlen(szPassword), 0); //散列口令
// 从散列表中派生密钥
CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey);
// 删除散列表
CryptDestroyHash(hHash);
hHash = 0;
}
//计算一次加密的数据字节数,必须为ENCRYPT_BLOCK_SIZE的整数倍dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
//如果使用块编码,则需要额外空间
if (ENCRYPT_BLOCK_SIZE > 1)
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
else
dwBufferLen = dwBlockLen;
//分配缓冲区
pbBuffer = (PBYTE)malloc(dwBufferLen);
//加密源文件并写入目标文件
do {
// 从源文件中读出dwBlockLen个字节
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
eof = feof(hSource);
//加密数据
CryptEncrypt(hKey, 0, eof, 0, pbBuffer, &dwCount, dwBufferLen);
// 将加密过的数据写入目标文件
fwrite(pbBuffer, 1, dwCount, hDestination);
} while (!feof(hSource));
//关闭文件、释放内存
fclose(hSource);
fclose(hDestination);
if (pbKeyBlob) {
free(pbKeyBlob);
pbKeyBlob = NULL;
}
printf("加密成功\n");
}
void CAPIDecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword) {
FILE* hSource = NULL;
FILE* hDestination = NULL;
INT eof = 0;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTKEY hXchgKey = 0;
HCRYPTHASH hHash = 0;
PBYTE pbKeyBlob = NULL;
DWORD dwKeyBlobLen;
PBYTE pbBuffer = NULL;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
hSource = fopen(szSource, "rb");// 打开源文件
if (!hSource) {
printf("fopen %s failed", szSource);
return;
}
hDestination = fopen(szDestination, "wb");//打开目标文件
//变量声明、文件操作同文件加密程序
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);
if (szPassword == NULL) {
// 口令为空,使用存储在加密文件中的会话密钥解密
// 读隐码的长度并分配内存
fread(&dwKeyBlobLen, sizeof(DWORD), 1, hSource);
pbKeyBlob = (PBYTE)malloc(dwKeyBlobLen);
// 从源文件中读隐码.
fread(pbKeyBlob, 1, dwKeyBlobLen, hSource);
// 将隐码输入CSP
CryptImportKey(hProv, pbKeyBlob, dwKeyBlobLen, 0, 0, &hKey);
}
else {
// 口令不为空, 使用从口令派生出的密钥解密文件
CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
CryptHashData(hHash, (BYTE*)szPassword, strlen(szPassword), 0);
CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey);
CryptDestroyHash(hHash);
hHash = 0;
}
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
if (ENCRYPT_BLOCK_SIZE > 1)
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
else
dwBufferLen = dwBlockLen;
pbBuffer = (PBYTE)malloc(dwBufferLen);
//解密源文件并写入目标文件
do {
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
eof = feof(hSource);
// 解密数据
CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount);
// 将解密过的数据写入目标文件
fwrite(pbBuffer, 1, dwCount, hDestination);
} while (!feof(hSource));
//关闭文件、释放内存
fclose(hSource);
fclose(hDestination);
if (pbKeyBlob) {
free(pbKeyBlob);
pbKeyBlob = NULL;
}
printf("解密成功\n");
}

Crypto++

安装

Github下载源码,打开“cryptest.sln”解决方案,编译“cryptlib”项目工程,找到“cryptopp890\x64\Output\Debug\cryptlib.lib”。

把cryptlib.lib和按照需要选择性地拷贝.h文件到当前工程下。

编译时需要检查编译cryptlib.lib和当前工程的“C\C++”->“代码生成”->“运行库”的选项是否一致,同时注意x86和x64一致。

HASH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <string>
#include "cryptopp\\sha.h"
#include "cryptopp\\files.h"
#include "cryptopp\\hex.h"
using namespace std;
using namespace CryptoPP;
#pragma comment(lib,"cryptopp\\cryptlib.lib")
string CalSHA1_ByFile(char* pszFileName) { //计算文件的SHA1
string value;
SHA1 sha1; //可相应改成SHA256 MD5等
FileSource(pszFileName, true, new HashFilter(sha1, new HexEncoder(new StringSink(value)))); //修改同上
return value;
};
string CalSHA1_ByMem(PBYTE pData, DWORD dwDataSize) { //计算数据的SHA1
string value;
SHA1 sha1;
StringSource(pData, dwDataSize, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));
return value;
};

AES

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
64
65
66
67
68
69
70
71
#include "cryptopp\\aes.h"
using namespace CryptoPP;
#pragma comment(lib,"cryptopp\\cryptlib.lib")
BOOL AES_Encrypt(PBYTE pOriginalData, DWORD dwOriginalDataSize, PBYTE pAESKey, DWORD dwAESKeySize, PBYTE* ppEncryptData, PDWORD pdwEncryptData) {
AESEncryption aesEncryptor; //加密器
unsigned char inBlock[AES::BLOCKSIZE]; //加密原文数据块
unsigned char outBlock[AES::BLOCKSIZE]; //加密后密文数据块
unsigned char xorBlock[AES::BLOCKSIZE]; //必须全0
DWORD dwOffset = 0;
PBYTE pEncryptData = NULL;
DWORD dwEncryptDataSize = 0;
DWORD dwQuotient = dwOriginalDataSize / AES::BLOCKSIZE;
DWORD dwRemaind = dwOriginalDataSize % AES::BLOCKSIZE;
if (0 != dwRemaind)
dwQuotient++;
dwEncryptDataSize = dwQuotient * AES::BLOCKSIZE;
pEncryptData = new BYTE[dwEncryptDataSize];
if (NULL == pEncryptData)
return FALSE;
aesEncryptor.SetKey(pAESKey, dwAESKeySize);
do {
::RtlZeroMemory(inBlock, AES::BLOCKSIZE);
::RtlZeroMemory(xorBlock, AES::BLOCKSIZE);
::RtlZeroMemory(outBlock, AES::BLOCKSIZE);
if (dwOffset <= (dwOriginalDataSize - AES::BLOCKSIZE))
::RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), AES::BLOCKSIZE);
else
::RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), (dwOriginalDataSize - dwOffset));
aesEncryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);
::RtlCopyMemory((PVOID)(pEncryptData + dwOffset), outBlock, AES::BLOCKSIZE);
dwOffset = dwOffset + AES::BLOCKSIZE;
dwQuotient--;
} while (0 < dwQuotient);
*ppEncryptData = pEncryptData;
*pdwEncryptData = dwEncryptDataSize;
return TRUE;
};
BOOL AES_Decrypt(PBYTE pEncryptData, DWORD dwEncryptDataSize, PBYTE pAESKey, DWORD dwAESKeySize, PBYTE* ppDecryptData, PDWORD pdwDecryptData) {
AESDecryption aesDecryptor;
unsigned char inBlock[AES::BLOCKSIZE];
unsigned char outBlock[AES::BLOCKSIZE];
unsigned char xorBlock[AES::BLOCKSIZE];
DWORD dwOffset = 0;
PBYTE pDecryptData = NULL;
DWORD dwDecryptDataSize = 0;
DWORD dwQuotient = dwEncryptDataSize / AES::BLOCKSIZE;
DWORD dwRemaind = dwEncryptDataSize % AES::BLOCKSIZE;
if (0 != dwRemaind)
dwQuotient++;
dwDecryptDataSize = dwQuotient * AES::BLOCKSIZE;
pDecryptData = new BYTE[dwDecryptDataSize];
if (NULL == pDecryptData)
return FALSE;
aesDecryptor.SetKey(pAESKey, dwAESKeySize);
do {
::RtlZeroMemory(inBlock, AES::BLOCKSIZE);
::RtlZeroMemory(xorBlock, AES::BLOCKSIZE);
::RtlZeroMemory(outBlock, AES::BLOCKSIZE);
if (dwOffset <= (dwDecryptDataSize - AES::BLOCKSIZE))
::RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), AES::BLOCKSIZE);
else
::RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), (dwEncryptDataSize - dwOffset));
aesDecryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);
::RtlCopyMemory((PVOID)(pDecryptData + dwOffset), outBlock, AES::BLOCKSIZE);
dwOffset = dwOffset + AES::BLOCKSIZE;
dwQuotient--;
} while (0 < dwQuotient);
*ppDecryptData = pDecryptData;
*pdwDecryptData = dwDecryptDataSize;
return TRUE;
};

RSA

用随机数生成公私钥对并保存到文件中,公钥加密时用公钥文件加密文件或数据,解密同理。代码自己去找,这里不写了。