熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

用CNG加密文件的簡單方法

2022-06-13   來源: .NET編程 

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


From:http://tw.wingwit.com/Article/program/net/201311/12871.html
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.