简介
文中用到了一些Cryptography API Next Generation(CNG)函数开发环境为Windows Vista下的Visual C++ SP标准版加上Windows SDK及CNG SDK
程序可适用于以下情况
? 在安全环境下保存文档但需要在不安全的媒质(如互联网)中传送
? 加密文件如图像MP各类文档
? 创建软件的产品密钥
需要注意的是CNG目前只支持Windows Vista且不能使用在Visual Basic及C#中要在Visual Studio中生成相应的Windows程序可能还需要Windows Vista SDK及CNG SDK两者都可以从微软官方网站下载获得
背景
我们最初是想在一个简单的GUI程序中使用CNG来加密文件需要以下三步
选择加密操作
选择需要加密的文件
选择加密密钥
相关程序
此处创建了一个MFC应用程序程序使用单文档界面在其中可选择待加解密的文件加密还是解密密码此外还有一个列表框用于显示其他信息
另外要在Visual C++ 中使用CNG SDK还需要进行如下的项目设置
在C/C++——General项右方的Additional Include Directories中添加以下目录C:\Program Files\Microsoft CNG Development Kit\Include
在Link——General项右方的Additional Library Directories中添加以下目录C:\Program Files\Microsoft CNG Development Kit\Lib\X
在Linker——Input项右方的Additional Dependencies中
添加bcryptlib
相关代码
在此使用CNG创建了类CMyCNGCryptFile它有三个公有方法
? EnumProviders枚举出注册的提供者
? CryptFile加密或解密一个文件
? GetLastError返回发生在CryptFile或EnumProviders中的最后一个错误
相关步骤如下打开算法提供者创建或导入一个密钥获取或设置算法属性执行操作关闭算法提供者
以下是CNG API
打开算法提供者
BCryptOpenAlgorithmProvider
导入密钥
BCryptGenerateSymmetricKey
创建密钥
BCryptCreateHash
BCryptHashData
BCryptFinishHash
BCryptGenerateSymmetricKey
获取或设置算法属性
BCryptGetProperty
BCryptSetProperty
执行加解密操作
BCryptEncrypt
BCryptDecrypt
枚举提供者
BCryptEnumRegisteredProviders
关闭算法提供者
BCryptCloseAlgorithmProvider
销毁密钥
BCryptDestroyKey
销毁哈希
BCryptDestroyHash
bool CryptFile(bool bEncrypt CString sFileToOpenCString sFileToCryptCString sKey)
这是从对话框中调用的主要方法它接受要执行的操作输入的文件输出的文件密钥作为参数步骤如下
用OpenMSPrimitiveProviderAES打开算法提供者
用CreateSymmetricKey_AES_CBC创建一个密钥或用CreateSymmetricKey_SHA_Hash导入一个密钥
获取相关文件的缓沖区
通过Crypt执行加解密操作输出中间文件并通过CryptLastByte获得最终的文件
保存加密数据到输出文件
OpenMSPrimitiveProviderAES方法打开一个到AES提供者的句柄
bool CMyCNGCryptFile::OpenMSPrimitiveProviderAES()
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
ntStatus = BCryptOpenAlgorithmProvider( &m_hAesAlg
BCRYPT_AES_ALGORITHM NULL );
switch (ntStatus)
{
case STATUS_SUCCESS:
return true;
case STATUS_INVALID_PARAMETER:
case STATUS_NO_MEMORY:
default:
//
}
return false;
}
CreateSymmetricKey_AES_CBC方法获取一个密钥并把它作为一个静态常数BYTE变量rgbAESKey存储在程序中第一步通过BCryptGetProperty取得算法属性接着用算法提供者句柄得到算法的实现细节如密钥大小及IV大小第二步把它分配在堆中并通过BCryptSetProperty修改算法的属性此处假定要使用BCRYPT_CHAIN_MODE_CBC我们将AES算法的BCRYPT_CHAINING_MODE属性设为BCRYPT_CHAIN_MODE_CBC现在我们就可通过BCryptGenerateSymmetricKey来创建一个短暂的密钥了
bool CMyCNGCryptFile::CreateSymmetricKey_AES_CBC(DWORD &cbKeyObject
DWORD &cbIV )
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
DWORD cbData = ;
cbKeyObject = ;
cbIV = ;
ntStatus = BCryptGetProperty(m_hAesAlg BCRYPT_OBJECT_LENGTH
(PBYTE)&cbKeyObject sizeof(DWORD) &cbData );
m_pbKeyObject = (PBYTE)HeapAlloc (GetProcessHeap () cbKeyObject);
ntStatus = BCryptGetProperty( m_hAesAlg BCRYPT_BLOCK_LENGTH
(PBYTE)&cbIV sizeof(DWORD) &cbData );
m_pbIV= (PBYTE) HeapAlloc (GetProcessHeap () cbIV);
memcpy(m_pbIV rgbIV cbIV);
ntStatus = BCryptSetProperty(m_hAesAlg BCRYPT_CHAINING_MODE
(PBYTE)BCRYPT_CHAIN_MODE_CBC sizeof(BCRYPT_CHAIN_MODE_CBC) );
ntStatus = BCryptGenerateSymmetricKey(m_hAesAlg &m_hKey m_pbKeyObject
cbKeyObject (PBYTE)rgbAESKey sizeof(rgbAESKey) );
return true;
}
CreateSymmetricKey_SHA_Hash方法从用户处获取一个密钥第一步通过BCryptOpenAlgorithmProvider打开一个新算法SHA使用SHA是因为提供者支持我们后面要用到的哈希接口接下来通过BCryptGetProperty得到算法属性再使用算法提供者句柄得到算法的实现细节如密钥大小及哈希大小之后再把它分配在堆中通过BCryptCreateHash为密钥创建哈希对象第二步使用BCryptHashData函数对数据缓沖区执行单向哈希并得到要用于BCryptHashData的哈希值为此使用了BCryptFinishHash现在就可通过BCryptSetProperty修改算法属性像上面一样把AES算法的BCRYPT_CHAINING_MODE属性设为BCRYPT_CHAIN_MODE_CBC最终将通过BCryptGenerateSymmetricKey创建一个短暂的密钥
bool CMyCNGCryptFile::CreateSymmetricKey_SHA_Hash(PCWSTR pwszText
DWORD cbKeyObject)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
BCRYPT_KEY_HANDLE hKey = NULL;
DWORD cbHashObject cbResult;
BYTE rgbHash[];
DWORD cbData = ;
ntStatus = BCryptOpenAlgorithmProvider(&m_hHashAlg
BCRYPT_SHA_ALGORITHMNULL);
ntStatus = BCryptGetProperty(m_hAesAlg BCRYPT_OBJECT_LENGTH
(PBYTE)&cbKeyObject sizeof(DWORD) &cbData );
ntStatus = BCryptGetProperty( m_hHashAlgBCRYPT_OBJECT_LENGTH
(PBYTE) &cbHashObjectsizeof(DWORD)&cbResult);
ntStatus = BCryptCreateHash(m_hHashAlg &m_hHash m_pbHashObject
cbHashObject NULL );
ntStatus = BCryptHashData( m_hHash (PBYTE)pwszText (ULONG)wcslen(
pwszText) );
ntStatus = BCryptFinishHash( m_hHash rgbHash sizeof(rgbHash) );
ntStatus = BCryptGenerateSymmetricKey( m_hAesAlg &hKey m_pbKeyObject
cbKeyObject rgbHash SYMM_KEY_SIZE_SECRET );
return true;
}
Crypt方法通过BCryptEncrypt与BCryptDecrypt函数执行加解密操作另外使用了相同长度的密文来加密数据
bool CMyCNGCryptFile::Crypt(bool bEncryptPUCHAR pbufFileToOpen
ULONG iBytesRead ULONG cbIV PBYTE pbufFileToSave DWORD& iBufToSave)
{
NTSTATUS ntStatus =STATUS_UNSUCCESSFUL;
DWORD cbCipherText = ;
if ( bEncrypt )
ntStatus = BCryptEncrypt(m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV pbufFileToSave iBytesRead &iBufToSave );
else
ntStatus = BCryptDecrypt(m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV pbufFileToSave iBytesRead &iBufToSave );
return false;
}
CryptLastByte方法使用了不同长度的密文来加密数据此处调用了BCryptEncrypt或BCryptDecrypt两次第一次是为了得到加密数据的大小第二次是得到密文
bool CMyCNGCryptFile::CryptLastByte(bool bEncryptPUCHAR pbufFileToOpen
ULONG iBytesRead ULONG cbIV PBYTE pbufFileToSave DWORD& iBufToSave)
{
NTSTATUS ntStatus= STATUS_UNSUCCESSFUL;
DWORD cbCipherText = ;
if (bEncrypt)
{
ntStatus = BCryptEncrypt(m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV NULL &cbCipherText
BCRYPT_BLOCK_PADDING);
ntStatus = BCryptEncrypt( m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV pbufFileToSave cbCipherText
&cbCipherTextBCRYPT_BLOCK_PADDING);
iBufToSave = cbCipherText;
}
else
{
ntStatus = BCryptDecrypt( m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV NULL &cbCipherText BCRYPT_BLOCK_PADDING);
ntStatus = BCryptDecrypt( m_hKey pbufFileToOpen iBytesRead NULL
m_pbIV cbIV pbufFileToSave cbCipherText &cbCipherText
BCRYPT_BLOCK_PADDING);
}
return false;
}
EnumProviders方法返回当前计算机上已安装的提供者调用BCryptEnumRegisteredProviders以获取有关已注册提供者的信息从pProviders中枚举提供者其为一个PCRYPT_PROVIDERS结构
bool CMyCNGCryptFile::EnumProviders(CStringList *lstRegisteredProviders)
{
ntStatus = BCryptEnumRegisteredProviders(&cbBuffer &pProviders);
for ( DWORD i = ; i < pProviders>cProviders; i++)
{
sProviderFormat(_T(%s\n)
pProviders>rgpszProviders[i]);
lstRegisteredProviders>AddHead(sProvider);
}
if (pProviders != NULL)
{
BCryptFreeBuffer(pProviders);
}
return true;
}
~CMyCNGCryptFile析构函数关闭算法提供者
删除所有的指针以防内存洩漏并销毁密钥哈希此处调用了BCryptCloseAlgorithmProvider来关闭算法提供者函数BCryptDestroyKey用于销毁密钥函数BCryptDestroyHash用于销毁哈希最后从PCRYPT_PROVIDERS结构的pProviders中枚举提供者
CMyCNGCryptFile::~CMyCNGCryptFile()
{
BCryptCloseAlgorithmProvider(m_hAesAlg);
BCryptDestroyKey(m_hKey);
HeapFree(GetProcessHeap() m_pbKeyObject);
HeapFree(GetProcessHeap() m_pbIV);
//Hash
BCryptDestroyHash(m_hHash);
free(m_pbHashObject);
BCryptCloseAlgorithmProvider(m_hHashAlg);
}