使用Microsoft CryptoAPI进行加密、解密、签名及验证
使用CryptoAPI编写一个文件保护程序,具有如下功能:
(1)给定明文文件,生成加密文件,同时产生文件的数字签名文件;
(2)给定密文文件,解密出明文文件,并验证签名的正确性。
代码:见所附main.cpp
一、 程序概况
a) 开发平台:Visual Studio 2005
b) 开发语言:C/C++
c) 使用密码库:CryptoAPI
二、 主要函数
a) 主函数
void main(void)
b) 加密文件
BOOL EncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
c) 解密文件
BOOL DecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
d) 签名文件
BOOL SignFile (PCHAR szSource, PCHAR szDestination);
e) 验证签名
BOOL VerifyFile (PCHAR szSource, PCHAR szDestination);
f) 错误处理
void HandleError(char *s);
三、 加密文件
a) 打开源文件
hSource = fopen(szSource,"rb")
b) 取得密钥容器(CSP)句柄
CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,0)
c) 根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件加密)
//创建一个Hash对象
CryptCreateHash(hCryptProv,CALG_MD5, 0, 0, &hHash)
//用用户输入的密码产生一个散列
CryptHashData(hHash, (BYTE *)szPassword, strlen(szPassword), 0)
//通过散列生成一个会话密钥
CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM,hHash, KEYLENGTH, &hKey))
//销毁Hash对象
CryptDestroyHash(hHash);
注: 会话密钥即对称密钥,用于对原文件进行加密;非对称密钥由于效率非常低,所以一般不用于对数据直接加密,而是对会话密钥进行加密,然后把它传送给对方。对 方通过非对称密钥解密获得这个会话密钥,然后再对数据文件进行解密。可以看出,一个会话密钥的生存期可以限制在这次通信中,即每次通信都用不同的会话密钥 加密,而非对称密钥则必须是长期使用的。在此例中,加解密过程中没有使用到非对称 RSA密钥对,而只在数字签名及验证使用它。
d) 加密数据文件
CryptEncrypt(
hKey, //密钥
0, //如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输
//入FALSE这里通过判断是否到文件尾来决定是否为最后一块
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount, //输入被加密数据实际长度,输出加密后数据长度
dwBufferLen) //pbBuffer的大小
注:查看完整代码时可以发现这是一个循环加密的过程,pbBuffer循环读入待加密文件的固定长度的内存块;当然你也可以将pbBuffer设得很大,一次读入整个文件,但那样浪费内存空间,而且影响扩展性(存在缓冲区溢出的可能)。
e) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
…
四、 解密文件
a) 打开加密文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件解密)(同上)
注: 这里要求用户输入的密码与加密时输入的密码相同。在实际应用中,这个所谓用户输入的“密码”其实只是一个产生密钥的种子,一旦产生完会话密钥,则用户完全 可以忘记当初输入的“密码”,接收方可以使用传过来的密钥直接对加密文件进行解密,而不用再重复一次“生成密钥”的过程。
d) 解密数据文件
CryptDecrypt(
hKey, //密钥
0, //如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输.
//入FALSE这里通过判断是否到文件尾来决定是否为最后一块。
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount)) //输入被加密数据实际长度,输出加密后数据长度
e) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)
五、 签名文件
a) 打开源文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 取得签名用的密钥句柄(非对称RSA密钥)
CryptGetUserKey(
hCryptProv, // 我们已经得到的CSP句柄
AT_SIGNATURE, // 这里想得到signature key pair
&hKey)) // 返回密钥句柄
d) 导出签名用密钥对的公钥,保存在pbKeyBlob中
CryptExportKey(hKey, NULL,PUBLICKEYBLOB, 0, pbKeyBlob,&dwBlobLen)
e) 计算数据文件的Hash值,保存在Hash对象hHash中
//生成一个空的Hash对象
CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash)
//计算数据文件的Hash值,保存在Hash对象中
CryptHashData(hHash,pbBuffer,dwCount,0)
f) 对数据文件的Hash值进行签名,数字签名保存在pbSignature中
CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen)
g) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)
六、 验证签名
a) 打开文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 导入 pbKeyBlob 公钥
CryptImportKey(hCryptProv, pbKeyBlob, dwBlobLen, 0, 0, &hPubKey)
注:必须是与签名时所用的私钥配对的公钥,在此例中,这个公钥在生成数字签名时已经导出到pbKeyBlob中。
d) 计算数据文件的Hash值,保存在Hash对象hHash中。(同上)
e) 验证数字签名
CryptVerifySignature(hHash, pbSignature, dwSigLen,hPubKey,NULL, 0)
f) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)
七、 实验结果
// 加密文件
Encrypt a file.
Enter the name of the file to be encrypted: 1.txt
Enter the name of the output file: 2.txt
Enter the password:123456
The source plaintext file, 1.txt, is open.
Destination file 2.txt is open.
A cryptographic provider has been acquired.
A hash object has been created.
The password has been added to the hash.
An encryption key is derived from the password hash.
Memory has been allocated for the buffer.
Encryption of the file 1.txt was a success.
The encrypted data is in file 2.txt.
//解密文件
Decrypt a file.
Enter the name of the file to be decrypted: 2.txt
Enter the name of the output file: 3.txt
Enter the password:123456
The source plaintext file, 2.txt, is open.
Destination file 3.txt is open.
A cryptographic provider has been acquired.
A hash object has been created.
The password has been added to the hash.
An encryption key is derived from the password hash.
Memory has been allocated for the buffer.
Decryption of the file 2.txt was a success.
The decrypted data is in file 3.txt.
//数字签名
Sign a file.
Enter the name of the file to be signed: 1.txt
Enter the name of the signature file: 11
The source plaintext file, 1.txt, is open.
Memory has been allocated for the buffer.
Destination file 11 is open.
A cryptographic provider has been acquired.
A signature key is available.
Size of the BLOB for the public key determined.
Memory has been allocated for the BLOB.
Contents have been written to the BLOB.
Hash object created.
The data buffer has been hashed.
Signature length 128 found.
Memory allocated for the signature.
pbSignature is the hash signature.
The hash object has been destroyed.
The signing phase of this program is completed.
Signature of the file 1.txt was a success.
The signature data is in file 11.
//验证签名
Verify a file and its signature.
Enter the name of the file to be verified: 1.txt
Enter the name of the signature file: 11
The source plaintext file, 1.txt, is open.
Memory has been allocated for the buffer.
A cryptographic provider has been acquired.
The key has been imported.
The hash object has been recreated.
The new has been created.
The signature has been verified.
Verification of the file 1.txt was a success.