Decryption is not working when using BCryptEncrypt and BCryptDecrypt in C++

john paul 41 Reputation points
2021-02-09T13:55:43.363+00:00

Hello All,

When I am trying to use encryption and decryption, decryption is not working.
Could someone please help me where I am missing?

#include <iostream>
#include <windows.h>
#include <assert.h>
#include <vector>
#include <Bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
using namespace std;
#define STATUS_SUCCESS                  ((NTSTATUS)0L)
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 main(int argc, char* 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(256);
        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(4);
    std::cout << "Original Data" << std::endl;
    for (auto val : origData)
    {
        std::cout << std::hex << (0xFF & val) << std::endl;
    }
    // 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,
            NULL,
            NULL,
            &encrypted[0], encrypted.size(),
            &bytesDone, 0
        );
        std::cout << "Encrypted Data" << std::endl;
        for (auto val : encrypted)
        {
            std::cout << std::hex << (0xFF & val) << std::endl;
        }
    }
    std::vector<BYTE> decrypted;
    {
        keyHandle = NULL;
        decrypted.resize(bytesDone);
        DWORD partSize = decrypted.size();
        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(256);
        authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
        bcryptResult = BCryptDecrypt
        (
            keyHandle,
            &encrypted[0],
            encrypted.size(),
            NULL,
            &contextIV[0], 
            contextIV.size(),
            &decrypted[0], 
            //partSize,
            encrypted.size(),
            &bytesDone, 
            0
        );
        if (bcryptResult == STATUS_SUCCESS)
        {
            std::cout << "bytes done:" << bytesDone << endl;
            std::cout << "Decrypted Data" << std::endl;
            for (auto val : decrypted)
            {
                std::cout << std::hex << (0xFF & val) << std::endl;
            }
        }
    }
    // Cleanup
    BCryptDestroyKey(keyHandle);
    BCryptCloseAlgorithmProvider(algHandle, 0);
    return 0;
}
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,523 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,637 questions
{count} votes

Accepted answer
  1. Drake Wu - MSFT 991 Reputation points
    2021-02-10T07:45:29.937+00:00

    Hi @john paul , It seems that you are using and modifying the example in this answer.

    After comparing the codes, I ‘ve got the following issues:

    1. You initialize the keyHandle to NULL before calling BCryptDecrypt, which will result in an error of STATUS_INVALID_HANDLE.
    2. Did not pass the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo to the BCryptDecrypt.
    3. Check another answer in that link, at point 7&8:

    Call BCryptEncrypt/BCryptDecrypt "N - 1" times

    • The amount of data passed to each call must be a multiple of the algorithm's block size.

    Call BCryptEncrypt/BCryptDecrypt one final time (with or without plain/cipher text input/output). The size of the input need not be a multiple of the algorithm's block
    size for this call. dwFlags is still set to 0
    .

    • Remove the BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG from the dwFlags field of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure using the &= syntax.

    Change the following snip works for me:

    std::vector<BYTE> decrypted;  
    {  
        //keyHandle = NULL;  
        decrypted.resize(bytesDone);  
        DWORD partSize = decrypted.size();  
        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(256);  
        authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;  
        bcryptResult = BCryptDecrypt  
        (  
            keyHandle,  
            &encrypted[0],  
            encrypted.size(),  
            &authInfo,  
            &contextIV[0],  
            contextIV.size(),  
            &decrypted[0],  
            //partSize,  
            decrypted.size(),  
            &bytesDone,  
            0  
        );  
        if (bcryptResult == STATUS_SUCCESS)  
        {  
            std::cout << "bytes done:" << bytesDone << endl;  
            std::cout << "Decrypted Data" << std::endl;  
            for (auto val : decrypted)  
            {  
                std::cout << std::hex << (0xFF & val) << std::endl;  
            }  
        }  
    }  
    

    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful