用CNG加密文件的简单方法

用CNG加密文件的简单方法


 


    简介
    文中用到了一些Cryptography API Next Generation(CNG)函数,开发环境为Windows Vista下的Visual C++ 2005 SP1标准版,加上Windows SDK及CNG SDK。
    程序可适用于以下情况:

    在安全环境下保存文档,但需要在不安全的媒质(如互联网)中传送。
    加密文件,如图像、MP3、各类文档。
    创建软件的产品密钥。


需要注意的是,CNG目前只支持Windows Vista,且不能使用在Visual Basic及C#中。要在Visual Studio中生成相应的Windows程序,可能还需要Windows Vista SDK及CNG SDK,两者都可以从微软官方网站下载获得。


    背景
    我们最初是想在一个简单的GUI程序中使用CNG来加密文件,需要以下三步:

1、    选择加密操作。
2、    选择需要加密的文件。
3、    选择加密密钥。


相关程序
    此处创建了一个MFC应用程序,程序使用单文档界面,在其中可选择待加解密的文件、加密还是解密、密码;此外,还有一个列表框,用于显示其他信息。
    另外,要在Visual C++ 2005中使用CNG SDK,还需要进行如下的项目设置:

1、    在“C/C++——General”项右方的“Additional Include Directories”中,添加以下目录:C:/Program Files/Microsoft CNG Development Kit/Include

 

2、    在“Link——General”项右方的“Additional Library Directories”中,添加以下目录:C:/Program Files/Microsoft CNG Development Kit/Lib/X86

 

3、    在“Linker——Input”项右方的“Additional Dependencies”中,添加“bcrypt.lib”。

 


    相关代码
    在此使用CNG创建了类CMyCNGCryptFile,它有三个公有方法:

    EnumProviders:枚举出注册的提供者。
    CryptFile:加密或解密一个文件。
    GetLastError:返回发生在CryptFile或EnumProviders中的最后一个错误。

相关步骤如下:1、打开算法提供者;2、创建或导入一个密钥;3、获取或设置算法属性;4、执行操作;5、关闭算法提供者。


    以下是CNG API:

打开算法提供者:
BCryptOpenAlgorithmProvider

导入密钥:
BCryptGenerateSymmetricKey

创建密钥:
BCryptCreateHash
BCryptHashData
BCryptFinishHash
BCryptGenerateSymmetricKey

获取或设置算法属性:
BCryptGetProperty
BCryptSetProperty

执行加解密操作:
BCryptEncrypt
BCryptDecrypt

枚举提供者:
BCryptEnumRegisteredProviders

关闭算法提供者:
BCryptCloseAlgorithmProvider

销毁密钥:
BCryptDestroyKey

销毁哈希:
BCryptDestroyHash


bool CryptFile(bool bEncrypt, CString sFileToOpen,CString sFileToCrypt,CString sKey)

    这是从对话框中调用的主要方法,它接受要执行的操作、输入的文件、输出的文件、密钥作为参数,步骤如下:

1、    用OpenMSPrimitiveProviderAES打开算法提供者。
2、    用CreateSymmetricKey_AES_CBC创建一个密钥,或用CreateSymmetricKey_SHA1_Hash导入一个密钥。
3、    获取相关文件的缓冲区。
4、    通过Crypt执行加解密操作,输出中间文件,并通过CryptLastByte获得最终的文件。
5、    保存加密数据到输出文件。


OpenMSPrimitiveProviderAES方法打开一个到AES提供者的句柄。

bool CMyCNGCryptFile::OpenMSPrimitiveProviderAES()
{
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    ntStatus  = BCryptOpenAlgorithmProvider( &m_hAesAlg,
        BCRYPT_AES_ALGORITHM, NULL, 0);
    switch (ntStatus)
    {
    case STATUS_SUCCESS:
        return true;
    case STATUS_INVALID_PARAMETER:
    case STATUS_NO_MEMORY:
    default:
    //... ...
    }
    return false;
}


    CreateSymmetricKey_AES_CBC方法获取一个密钥,并把它作为一个静态常数BYTE变量rgbAES128Key存储在程序中。第一步,通过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    = 0;

    cbKeyObject    = 0;
    cbIV  = 0;

    ntStatus = BCryptGetProperty(m_hAesAlg, BCRYPT_OBJECT_LENGTH,
        (PBYTE)&cbKeyObject,  sizeof(DWORD), &cbData, 0);
    ...
    m_pbKeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbKeyObject);
    ...
    ntStatus = BCryptGetProperty( m_hAesAlg, BCRYPT_BLOCK_LENGTH,
        (PBYTE)&cbIV, sizeof(DWORD), &cbData, 0);
    ...
    m_pbIV= (PBYTE) HeapAlloc (GetProcessHeap (), 0, cbIV);

    memcpy(m_pbIV, rgbIV, cbIV);

    ntStatus = BCryptSetProperty(m_hAesAlg, BCRYPT_CHAINING_MODE,
        (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
    ...
    ntStatus = BCryptGenerateSymmetricKey(m_hAesAlg, &m_hKey, m_pbKeyObject,
        cbKeyObject, (PBYTE)rgbAES128Key, sizeof(rgbAES128Key), 0);
    ...
    return true;

}


    CreateSymmetricKey_SHA1_Hash方法从用户处获取一个密钥。第一步,通过BCryptOpenAlgorithmProvider打开一个新算法SHA1,使用SHA1是因为提供者支持我们后面要用到的哈希接口。接下来通过BCryptGetProperty得到算法属性,再使用算法提供者句柄得到算法的实现细节,如密钥大小及哈希大小,之后再把它分配在堆中,通过BCryptCreateHash为密钥创建哈希对象;第二步,使用BCryptHashData函数对数据缓冲区执行单向哈希,并得到要用于BCryptHashData的哈希值,为此,使用了BCryptFinishHash。现在,就可通过BCryptSetProperty修改算法属性,像上面一样,把AES算法的BCRYPT_CHAINING_MODE属性设为BCRYPT_CHAIN_MODE_CBC,最终将通过BCryptGenerateSymmetricKey创建一个短暂的密钥。

bool CMyCNGCryptFile::CreateSymmetricKey_SHA1_Hash(PCWSTR pwszText,
    DWORD cbKeyObject)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BCRYPT_KEY_HANDLE    hKey = NULL;
   
    DWORD               cbHashObject, cbResult;
    BYTE                rgbHash[20];
    DWORD                cbData    = 0;

    ntStatus = BCryptOpenAlgorithmProvider(&m_hHashAlg,
                     BCRYPT_SHA1_ALGORITHM,NULL,0);
    ...
    ntStatus = BCryptGetProperty(m_hAesAlg, BCRYPT_OBJECT_LENGTH,
                    (PBYTE)&cbKeyObject,  sizeof(DWORD), &cbData, 0);
    ...   
    ntStatus = BCryptGetProperty( m_hHashAlg,BCRYPT_OBJECT_LENGTH,
                    (PBYTE) &cbHashObject,sizeof(DWORD),&cbResult,0);
    ...
    ntStatus = BCryptCreateHash(m_hHashAlg, &m_hHash, m_pbHashObject,
                    cbHashObject, NULL, 0, 0 );

    ntStatus = BCryptHashData( m_hHash,  (PBYTE)pwszText, (ULONG)wcslen(
        pwszText), 0);

    ntStatus = BCryptFinishHash( m_hHash, rgbHash, sizeof(rgbHash), 0);
    ...
    ntStatus = BCryptGenerateSymmetricKey( m_hAesAlg, &hKey,  m_pbKeyObject,
                cbKeyObject, rgbHash, SYMM_KEY_SIZE_SECRET,  0  );
    ...
    return true;
}


    Crypt方法通过BCryptEncrypt与BCryptDecrypt函数执行加解密操作,另外,使用了相同长度的密文来加密数据。

bool CMyCNGCryptFile::Crypt(bool bEncrypt,PUCHAR pbufFileToOpen,
    ULONG iBytesRead, ULONG cbIV, PBYTE pbufFileToSave, DWORD& iBufToSave)
{
   
    NTSTATUS ntStatus =STATUS_UNSUCCESSFUL;   
    DWORD        cbCipherText        = 0;

    if ( bEncrypt )                    
        ntStatus = BCryptEncrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL,
                   m_pbIV, cbIV, pbufFileToSave, iBytesRead, &iBufToSave, 0);

    else       
        ntStatus = BCryptDecrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL,
                   m_pbIV, cbIV, pbufFileToSave, iBytesRead, &iBufToSave,  0);

    ... 
    return false;
}


    CryptLastByte方法使用了不同长度的密文来加密数据,此处调用了BCryptEncrypt或BCryptDecrypt两次,第一次是为了得到加密数据的大小,第二次是得到密文。

bool CMyCNGCryptFile::CryptLastByte(bool bEncrypt,PUCHAR pbufFileToOpen,
    ULONG iBytesRead, ULONG cbIV, PBYTE pbufFileToSave, DWORD& iBufToSave)
{
    NTSTATUS ntStatus= STATUS_UNSUCCESSFUL;
    DWORD        cbCipherText        = 0;

    if (bEncrypt)
    {
        ntStatus = BCryptEncrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL,
                     m_pbIV, cbIV, NULL, 0, &cbCipherText,
                     BCRYPT_BLOCK_PADDING);
        ...
        ntStatus = BCryptEncrypt( m_hKey, pbufFileToOpen, iBytesRead, NULL,
                     m_pbIV, cbIV, pbufFileToSave, cbCipherText,
                     &cbCipherText,BCRYPT_BLOCK_PADDING);
        iBufToSave        =    cbCipherText;
    ...   
    }
    else
    {
        ntStatus = BCryptDecrypt( m_hKey, pbufFileToOpen, iBytesRead, NULL,
        m_pbIV, cbIV, NULL, 0, &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 = 0; i < pProviders->cProviders; i++)
    {
        sProvider.Format(_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,0);
    BCryptDestroyKey(m_hKey);
    HeapFree(GetProcessHeap(), 0, m_pbKeyObject);
    HeapFree(GetProcessHeap(), 0, m_pbIV);
    //Hash
    BCryptDestroyHash(m_hHash);
    free(m_pbHashObject);
    BCryptCloseAlgorithmProvider(m_hHashAlg,0);
}

你可能感兴趣的:(加密,算法,windows,解密,null,Cryptography)