2015-06-08 56 views
10

使用Windows CNG API,我可以在GCM模式下使用AES對身份驗證的各個數據塊進行加密和解密。我現在想要在一行中加密和解密多個緩衝區。如何在GCM模式下使用AES連接BCryptEncrypt和BCryptDecrypt調用?

documentation for CNG,以下方案支持:

如果輸入到加密或解密被分散在多個 緩衝區,則必須調用鏈向BCryptEncrypt和 BCryptDecrypt功能。鏈接通過在dwFlags成員中設置 BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG標誌來指示。

如果我的理解正確,這意味着我可以在多個緩衝區上依次調用BCryptEncrypt,並在最後獲得組合緩衝區的認證標記。同樣,我可以在多個緩衝區上依次調用BCryptDecrypt,同時推遲實際的身份驗證檢查直到結束。我無法讓它工作,但它看起來像dwFlags的值被忽略。無論何時使用BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG,我得到的返回值爲0xc000a002,這等於ntstatus.h中定義的STATUS_AUTH_TAG_MISMATCH

即使參數pbIV被標記爲輸入/輸出,元素由參數pbIV不要被BCryptEncrypt()修改指向。這是預期的嗎?我還查看了BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO結構中指向pPaddingInfo指針的字段pbNonce,但該指針也沒有被修改。我也試着「手動」推進IV,根據櫃檯方案自己修改內容,但這也沒有幫助。

成功鏈接BCryptEncrypt和/或BCryptDecrypt函數的正確過程是什麼?

回答

6

我設法讓它工作。這似乎是MSDN中的問題,應該提及設置BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG而不是BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG

#include <windows.h> 
#include <assert.h> 
#include <vector> 
#include <Bcrypt.h> 
#pragma comment(lib, "bcrypt.lib") 

std::vector<BYTE> MakePatternBytes(size_t a_Length) 
{ 
    std::vector<BYTE> result(a_Length); 
    for (size_t i = 0; i < result.size(); i++) 
    { 
     result[i] = (BYTE)i; 
    } 

    return result; 
} 

std::vector<BYTE> MakeRandomBytes(size_t a_Length) 
{ 
    std::vector<BYTE> result(a_Length); 
    for (size_t i = 0; i < result.size(); i++) 
    { 
     result[i] = (BYTE)rand(); 
    } 

    return result; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    NTSTATUS bcryptResult = 0; 
    DWORD bytesDone = 0; 

    BCRYPT_ALG_HANDLE algHandle = 0; 
    bcryptResult = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, 0, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptOpenAlgorithmProvider"); 

    bcryptResult = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptSetProperty(BCRYPT_CHAINING_MODE)"); 

    BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths; 
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &bytesDone, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_AUTH_TAG_LENGTH)"); 

    DWORD blockLength = 0; 
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &bytesDone, 0); 
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_BLOCK_LENGTH)"); 

    BCRYPT_KEY_HANDLE keyHandle = 0; 
    { 
     const std::vector<BYTE> key = MakeRandomBytes(blockLength); 
     bcryptResult = BCryptGenerateSymmetricKey(algHandle, &keyHandle, 0, 0, (PUCHAR)&key[0], key.size(), 0); 
     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGenerateSymmetricKey"); 
    } 

    const size_t GCM_NONCE_SIZE = 12; 
    const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE); 
    const std::vector<BYTE> origData = MakePatternBytes(256); 

    // Encrypt data as a whole 
    std::vector<BYTE> encrypted = origData; 
    std::vector<BYTE> authTag(authTagLengths.dwMinLength); 
    { 
     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; 
     BCRYPT_INIT_AUTH_MODE_INFO(authInfo); 
     authInfo.pbNonce = (PUCHAR)&origNonce[0]; 
     authInfo.cbNonce = origNonce.size(); 
     authInfo.pbTag = &authTag[0]; 
     authInfo.cbTag = authTag.size(); 

     bcryptResult = BCryptEncrypt 
      (
      keyHandle, 
      &encrypted[0], encrypted.size(), 
      &authInfo, 
      0, 0, 
      &encrypted[0], encrypted.size(), 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptEncrypt"); 
     assert(bytesDone == encrypted.size()); 
    } 

    // Decrypt data in two parts 
    std::vector<BYTE> decrypted = encrypted; 
    { 
     DWORD partSize = decrypted.size()/2; 

     std::vector<BYTE> macContext(authTagLengths.dwMaxLength); 

     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; 
     BCRYPT_INIT_AUTH_MODE_INFO(authInfo); 
     authInfo.pbNonce = (PUCHAR)&origNonce[0]; 
     authInfo.cbNonce = origNonce.size(); 
     authInfo.pbTag = &authTag[0]; 
     authInfo.cbTag = authTag.size(); 
     authInfo.pbMacContext = &macContext[0]; 
     authInfo.cbMacContext = macContext.size(); 

     // IV value is ignored on first call to BCryptDecrypt. 
     // This buffer will be used to keep internal IV used for chaining. 
     std::vector<BYTE> contextIV(blockLength); 

     // First part 
     authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; 
     bcryptResult = BCryptDecrypt 
      (
      keyHandle, 
      &decrypted[0*partSize], partSize, 
      &authInfo, 
      &contextIV[0], contextIV.size(), 
      &decrypted[0*partSize], partSize, 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt"); 
     assert(bytesDone == partSize); 

     // Second part 
     authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; 
     bcryptResult = BCryptDecrypt 
      (
      keyHandle, 
      &decrypted[1*partSize], partSize, 
      &authInfo, 
      &contextIV[0], contextIV.size(), 
      &decrypted[1*partSize], partSize, 
      &bytesDone, 0 
      ); 

     assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt"); 
     assert(bytesDone == partSize); 
    } 

    // Check decryption 
    assert(decrypted == origData); 

    // Cleanup 
    BCryptDestroyKey(keyHandle); 
    BCryptCloseAlgorithmProvider(algHandle, 0); 

    return 0; 
} 
+0

感謝您的建議,這是有道理的。它似乎並沒有工作,我添加了一些關於這個問題的附加文本。 –

+0

我已經完全改變了我的答案。 – Codeguard

+0

太棒了,非常感謝。一個評論:'trashIV'實際上保持了先進的IV,所以在鏈接模式下正常運行是必需的。我的錯誤是我使IV與nonce的大小相同(除了使用錯誤的dwFlags值)。 –

相關問題