网络安全

位置:IT落伍者 >> 网络安全 >> 浏览文章

用CNG加密文件的简单方法


发布日期:2022年11月18日
 
用CNG加密文件的简单方法

简介

文中用到了一些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);

}

               

上一篇:了解网页中IFRAME框架挂马

下一篇:五种常见的ASP.NET应用程序安全缺陷