KPI之CryptoAPI-概述
微软的CryptoAPI加密技术
微软的CryptoAPI是PKI推荐使用的加密API。其功能是为应用程序开发者提供在Win32环境下使用加密、验证等安全服务时的标准加密接口。CryptoAPI处于应用程序和CSP之间
CryptoAPI架构
CryptoAPI共有五部分组成:简单消息函数、低层消息函数、基本加密函数、证书编解码函数和证书库管理函数。前三者可用于对敏感信息进行加密或签名处理,可保证网络传输信心的私有性;后两者通过对证书的使用,可保证网络信息交流中的认证性。
基本加密函数:为开发加密应用程序提供了足够灵活的空间。所有CSP 的通讯都是通过这些函数。一个CSP 是实现所有加密操作的独立模块。在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作。如果使用多于一个以上的CSP,在加密函数调用中就要指定所需的CSP。基本加密函数包含了以下几种:
-
服务提供者函数:应用程序使用服务提供者函数来连接和断开一个CSP。
-
密钥的产生和交换函数:密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。
-
编码/解码函数:用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
-
哈希和数字签名函数:这些函数在应用程序中完成计算哈希、创建和校验数字签名。
证书和证书库函数:这组函数管理、使用和取得证书、证书撤销列表和证书信任列表。这些函数可以分成一下几组:
-
证书库函数:一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了对库获得、枚举、验证和使用证书库里的信息。
-
维护函数
-
证书函数:下列函数是针对于证书的。大多数函数都是处理CRL 和CTL 的。
-
证书撤销列表函数:
-
证书信任列表函数:
-
扩展属性函数:
证书验证函数:证书验证是通过CTL 和证书列表进行的:
-
使用CTL 的函数:
-
证书链验证函数:
消息函数:CryptoAPI 消息函数包括两组——低级消息函数和简化消息函数:
-
低级消息函数直接和PKCS#7 消息工作。这些函数对传输的PKCS#7 数据进行编码,对接收到的PKCS#7 数据进行解码,并且对接收到的消息进行解密和验证。
-
简化消息函数是比较高级的函数,是对几个低级消息函数和证书函数的封装,用来执行指定任务。这些函数在完成一个任务时,减少了函数调用的数量,因此简化了CryptoAPI的使用。
辅助函数
-
数据管理函数
-
数据转换函数
-
增强密钥用法函数
-
密钥标示函数
-
证书库回调函数
-
OID 支持函数
-
远程对象恢复函数
-
PFX 函数
CSP:真正实行加密的独立模块,既可以由软件实现也可以由硬件实现。但是必须符合CryptoAPI接口的规范。
创建密钥容器,得到CSP句柄
每一个CSP都有一个名字和一个类型,并且名字保证唯一。所以可以通过名字和类型得到一个CSP。然而,要想加密肯定需要密钥,密钥放在密钥容器。密钥容器并不是一开始就存在的,需要用户去创建。
下面是创建容器的代码:
1 | if(CryptAcquireContext( |
实现代码:
1 |
|
使用CryptoAPI加密解密
windows crypto API提供了对称加密和非对称加密,并且提供了各种加密、解密的算法,要使用相应的算法进行加密解密,只需要对生成密钥的函数的相关参数改变一下即可。
PKCS#11及CSP接口标准
PKCS#11(简称P11)是针对密码设备的接口指令标准。P11模型中重要的概念之一是slot,也称为槽。一个slot为一个密码设备对象。某个打开的slot会话称之为session。Session之间存在不同的验证权限。而同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。另外一个重要的概念是对象。P11中支持几种重要的对象,如公钥、私钥、对称密钥,数据对象等。
PKCS#11创建和支持下列对象:
PKCS#11的对象可根据其生命期长短的不同分成两大类:
- 持久存储的类对象,这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象;
- 会话对象,这类对象只存在于运行时建立的特定会话(Session对象)当中,一旦会话结束,这类对象也跟着被删除。
决定对象生命期的模板属性是CKA_TOKEN,这是个布尔值,所有的对象都有这一属性。当该值为TRUE时,该对象将被保存到Key内的存储空间,否则,该对象保存在会话空间中,当会话结束后,该对象即销毁。
P11标准颁发了70余条指令。其中部分指令简介如下表:
PKCS#11架构
GMT 0016-2012
-
这个标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。
层次关系:智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图: -
设备的应用结构:一个设备中存在设备认证密钥和多个应用,应用之间相互独立。设备的逻辑结构如下图:
-
设备管理系列函数:
-
访问控制系列函数
-
应用管理函数
-
容器管理系列函数
-
密码服务系列函数
GMT 0018-2012
本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。
范围:本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准,适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。
密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置:在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终瑞等设备组成,通过本标准规定的密码设备应用接口向通用密码服务层提供基础密码服务。如下图:
基础密码服务包括密钥生成、单一的密码运算、文件管理等服务。
本标准采用C语言描述接口函数,无特别说明时,函数中参数的长度单位均为字节数。
-
设备管理类函数:
-
打开设备:SDF_OpenDevice
-
关闭设备:SDF_CloseDevice
-
创建会话:SDF_OpenSession
-
关闭会话:SDF_CloseSession
-
获取设备信息:SDF_GetDeviceInfo
-
产生随机数:SDF_GenerateRandom
-
获取私钥使用权限:SDF_GetPrivateKeyAccessRight
-
释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight
-
密钥管理类函数:
-
导出 RSA 签名公钥:SDF_ExportSignPublicKey_RSA
-
导出 RSA 加密公钥:SDF_ExportEncPublicKey_RSA
-
产生RSA非对称密钥对并输出:SDF_GenerateKeyPair_RSA
-
生成会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK_RSA
-
生成会话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_RSA - 导人会话密钥并用内部RSA私钥解密:SDF_ImportKeyWithISK_RSA
-
基于 RSA 算法的数宇倍封转换:SDF_ExchangeDigitEnvelopeBaseOnRSA
-
导出 ECC 签名公钥:SDF_ExportSignPublicKey_ECC
-
导出 ECC 加密公钥:SDF_ExportEncPublicKey_ECC
-
产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ECC
-
生成会话密钥并用内部ECC公钥加密输岀:SDF_GenerateKeyWithIPK_ECC - 生成会话密钥并用外部ECC公钥加密输出:SDF_GenerateKeyWithEPK_ECC
-
导入会话密钥并用内部ECC私钥解密:SDFJmportKeyWithlSKJECC
-
生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
-
计算会话密钥:SDF_GenerateKey WithECC
-
产生协商数据并计算会话密钥:SDF—GenerateAgreementDataAndKeyWithECC
-
基于 ECC算法的数字信封转换:SDF_ExchangeDigitEnvelopeBaseOnECC
-
生成会话密钥并用密钥加密密钥加密输出: SDF_GenerateKeyWithKEK
-
导入会话密钥并用密钥加密密钥解密:SDF_ImportKeyWithKEK
-
销毁会话密钥:SDF_DestroyKey
-
非对称算法运算类函数
-
内部公钥 RSA 运算:SDF_ExternalPublicKeyOperation_RSA
-
内部公钥 RSA 运算:SDF_InternalPublicKeyOperation_RSA
-
内部私钥 RSA 运算:SDF_InternalPrivateKeyOperation_RSA
-
外部密钥 ECC 验证:SDF_ExternalVerify_ECC
-
内部密钥 ECC 签名:SDF_InternalSign_ECC
-
内部密钥 ECC 验证:SDF_InternalVerify_ECC
-
外部密钥 ECC 加密:SDF_ExternalEncrypt_ECC
-
对称算法运算类函数
-
对称加密:SDF_Encrypt
-
对称解密:SDF_Decrypt
-
计算MAC:SDF_CalculateMAC
-
杂凑运算类函数
-
杂凑运算初始化:SDF_HashInit
-
多包杂凑运算:SDF_HashUpdate
-
杂凑运算结束:SDF_HashFinal
-
安全要求
-
基于本标准设计、开发的密码设备在密钥管理方面,应满足以下要求:
- 设备密钥的使用不对应用系统开放;
- 密钥必须用安全的方法产生并存储;
- 在任何时间、任何情况下,除公钥外的密钥均不能以明文形式出现在密码设备外;
- 密码设备内部存储的密钥应具备有效的密钥保护机制,防止解剖、探测和非法读取;
- 密码设备内部存储的密钥应具备权限控制机制,防止非法使用和导出。
-
密码服务要求:
- 使用的密码算法应得到国家密码主管部门的批准;
- 使用国家密码主管部门认可的密码算法芯片;
- 本标准所列的所有接口函数均应能被应用系统任意调用。
-
设备状态要求:
- 密码设备应具有初始和就绪两个状态;
- 未安装设备密钥的密码设备应处于初始状态,已安装设备密钥的密码设备应处于就绪状态;
- 在初始状态下,除可读取设备信息、设备密钥的生成或恢复操作外,不能执行任何操作,生成或恢复设备密钥后,密码设备处于就绪状态;
- 在就绪状态下,除设备密钥的生成或恢复操作外,应能执行任何操作;
- 在就绪状态下进行的密钥操作,设备操作员应经过密码设备的认证。
-
其他要求:
- 密码设备应有安全机制和措施,保证密钥在生成、安装、导入、存储、备份.恢复及销毁整个生存期间的安全,此安全机制可由设备厂商自行设计实现。
龙脉GM3000Key调用
-
Windows crypticAPI实现加密
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
void HandleError(char *s);
BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
int main()
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Encrypt a file. \n\n");
printf("Enter the name of the file to be encrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(EncryptFile(szSource, szDestination, szPassword))
{
printf("Encryption of the file %s was a success. \n", szSource);
printf("The encrypted data is in file %s.\n",szDestination);
}
else
{
HandleError("Error encrypting file!");
}
}
extern BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
if(hSource = fopen(szSource,"rb"))
{
printf("The source plaintext file, %s, is open. \n", szSource);
}
else
{
HandleError("Error opening source plaintext file!");
}
if(hDestination = fopen(szDestination,"wb"))
{
printf("Destination file %s is open. \n", szDestination);
}
else
{
HandleError("Error opening destination ciphertext file!");
}
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("A cryptographic provider has been acquired. \n");
}
else
{
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))//创建密钥容器
{
//创建密钥容器成功,并得到CSP句柄
printf("A new key container has been created.\n");
}
else
{
HandleError("Could not create a new key container.\n");
}
}
if(CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
printf("A hash object has been created. \n");
}
else
{
HandleError("Error during CryptCreateHash!\n");
}
if(CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
printf("The password has been added to the hash. \n");
}
else
{
HandleError("Error during CryptHashData. \n");
}
if(CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
printf("An encryption key is derived from the password hash. \n");
}
else
{
HandleError("Error during CryptDeriveKey!\n");
}
CryptDestroyHash(hHash);
hHash = NULL;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
if(ENCRYPT_BLOCK_SIZE > 1)
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
else
dwBufferLen = dwBlockLen;
if(pbBuffer = (BYTE *)malloc(dwBufferLen))
{
printf("Memory has been allocated for the buffer. \n");
}
else
{
HandleError("Out of memory. \n");
}
do
{
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
if(ferror(hSource))
{
HandleError("Error reading plaintext!\n");
}
if(!CryptEncrypt(
hKey,
0,
feof(hSource),
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount, //输入被加密数据实际长度,输出加密后数据长度
dwBufferLen)) //pbBuffer的大小。
{
HandleError("Error during CryptEncrypt. \n");
}
fwrite(pbBuffer, 1, dwCount, hDestination);
if(ferror(hDestination))
{
HandleError("Error writing ciphertext.");
}
}
while(!feof(hSource));
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return(TRUE);
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. \n");
fprintf(stderr,"%s\n",s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
} -
Windows crypticAPI实现解密
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
void HandleError(char *s);
BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
int main()
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Decrypt a file. \n\n");
printf("Enter the name of the file to be decrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(!DecryptFile(szSource, szDestination, szPassword))
{
printf("\nError decrypting file. \n");
}
else
{
printf("\nDecryption of file %s succeeded. \n", szSource);
printf("The decrypted file is %s .\n",szDestination);
}
}
extern BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
BOOL status = FALSE;
if(!(hSource = fopen(szSource,"rb")))
{
HandleError("Error opening ciphertext file!");
}
if(!(hDestination = fopen(szDestination,"wb")))
{
HandleError("Error opening plaintext file!");
}
if(!CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
HandleError("Error during CryptAcquireContext!");
}
if(!CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
HandleError("Error during CryptCreateHash!");
}
if(!CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
HandleError("Error during CryptHashData!");
}
if(!CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
HandleError("Error during CryptDeriveKey!");
}
CryptDestroyHash(hHash);
hHash = 0;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
dwBufferLen = dwBlockLen;
if(!(pbBuffer = (BYTE *)malloc(dwBufferLen)))
{
HandleError("Out of memory!\n");
}
do
{
dwCount = fread(
pbBuffer,
1,
dwBlockLen,
hSource);
if(ferror(hSource))
{
HandleError("Error reading ciphertext!");
}
if(!CryptDecrypt(
hKey,
0,
feof(hSource),
0,
pbBuffer,
&dwCount))
{
HandleError("Error during CryptDecrypt!");
}
fwrite(
pbBuffer,
1,
dwCount,
hDestination);
if(ferror(hDestination))
{
HandleError("Error writing plaintext!");
}
}
while(!feof(hSource));
status = TRUE;
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return status;
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. \n");
fprintf(stderr,"%s\n",s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
} // End of HandleError -
实现MD5
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
BOOL GetContentMD5(
BYTE *pszFilePath,
BOOL bFile,
BOOL bUpperCase,
TCHAR *pszResult,
DWORD &dwStatus)
{
BOOL bResult = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HANDLE hFile = NULL;
BYTE rgbFile[BUFSIZE];
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN];
DWORD cbHash = 0;
CHAR rgbDigitsL[] = "0123456789abcdef";
CHAR rgbDigitsU[] = "0123456789ABCDEF";
CHAR *rgbDigits = bUpperCase ? rgbDigitsU : rgbDigitsL;
TCHAR szResult[MD5LEN*2+1] = {0};
dwStatus = 0;
bResult = CryptAcquireContext(&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT);
CHECK_NULL_RET(bResult);
bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
CHECK_NULL_RET(bResult);
if (bFile)
{
hFile = CreateFile((TCHAR *)pszFilePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
CHECK_NULL_RET(!(INVALID_HANDLE_VALUE == hFile));
while (bResult = ReadFile(hFile, rgbFile, BUFSIZE,
&cbRead, NULL))
{
if (0 == cbRead)
{
break;
}
bResult = CryptHashData(hHash, rgbFile, cbRead, 0);
CHECK_NULL_RET(bResult);
}
}
else
{
bResult = CryptHashData(hHash, pszFilePath, strlen((CHAR *)pszFilePath), 0);
CHECK_NULL_RET(bResult);
}
cbHash = MD5LEN;
if (bResult = CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
{
TCHAR szTmpBuff[3] ;
for (DWORD i = 0; i < cbHash; i++)
{
sprintf (szTmpBuff, TEXT("%c%c"), rgbDigits[rgbHash[i]>>4],
rgbDigits[rgbHash[i] & 0xf]);
lstrcat(szResult, szTmpBuff);
}
bResult = TRUE;
}
Exit0:
dwStatus = GetLastError();
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
lstrcpy(pszResult, szResult);
return bResult;
}
int main(int argc, char* argv[])
{
DWORD dwStatus = 0;
TCHAR szResult[MD5LEN*2+1] = {0};
TCHAR szFilePath[] = TEXT("C:\Users\Eulalia\Desktop\信息安全系统设计与实现下\code\1234.dll");
CHAR szContent[] = "explorer.exe";
GetContentMD5((BYTE *)szFilePath,
TRUE, TRUE, szResult, dwStatus);
MessageBox(NULL, szResult, TEXT("该文件的MD5"), MB_OK);
return 0;
} -
PKCS端口
-
DES
-
DES3
- RC2
- RC4
- AES
-