How to call BCryptEncrypt to encrypt the data with AES-256-CBC with block padding and the data is segregated in multiple variables Windows CNG API

Pavan 101 Reputation points
2021-02-24T13:35:31.01+00:00

I want to encrypt the data with AES-256-CBC with block padding and data is segregated in multiple variables i.e. I want to call BCryptEncrypt multiple times and at the end I will combine all the ciphertext into one buffer.

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib") 

    #define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

    #define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)


    const PBYTE pbPlainText1="The quick brown ";
    const PBYTE pbPlainText2="The quick brown fox jumps over t";
    const PBYTE pbPlainText3="he lazy dog";

    static const BYTE rgbIV[] = "1234567887654321";

    static const BYTE rgbAES256Key[] = "0123456789abcdefghijklmnopqrstuv";

    int ival_len = 16;   //IV BUFF SIZE
    int key_len = 32;    //KEY LEN


    void __cdecl wmain(
                       int                      argc, 
                       __in_ecount(argc) LPWSTR *wargv)
    {

        BCRYPT_ALG_HANDLE       hAesAlg                     = NULL;
        BCRYPT_KEY_HANDLE       hKey                        = NULL;
        NTSTATUS                status                      = STATUS_UNSUCCESSFUL;
        DWORD                   cbCipherText                = 0, 
                                cbPlainText                 = 0,
                                cbData                      = 0, 
                                cbKeyObject                 = 0,
                                cbBlockLen                  = 0,
                                cbBlob                      = 0;
        PBYTE                   pbCipherText                = NULL,
                                pbPlainText                 = NULL,
                                pbKeyObject                 = NULL,
                                pbIV                        = NULL,
                                pbBlob                      = NULL,
                                cipherText ;
     DWORD cbKeyObject_d  = 0,cbBlockLen_d = 0,cbData_d=0;   
        BCRYPT_ALG_HANDLE       hAesAlg_d = NULL;
        BCRYPT_KEY_HANDLE       hKey_d  = NULL; 
     PBYTE   pbKeyObject_d   = NULL, pbIV_d = NULL;

        UNREFERENCED_PARAMETER(argc);
        UNREFERENCED_PARAMETER(wargv);

        // Open an algorithm handle.
        if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
                                                    &hAesAlg,
                                                    BCRYPT_AES_ALGORITHM,
                                                    NULL,
                                                    0)))
        {
            wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
            goto Cleanup;
        }

        // Calculate the size of the buffer to hold the KeyObject.
        if(!NT_SUCCESS(status = BCryptGetProperty(
                                            hAesAlg, 
                                            BCRYPT_OBJECT_LENGTH, 
                                            (PBYTE)&cbKeyObject, 
                                            sizeof(DWORD), 
                                            &cbData, 
                                            0)))
        {
            wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
            goto Cleanup;
        }

        // Allocate the key object on the heap.
        pbKeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbKeyObject);
        if(NULL == pbKeyObject)
        {
            wprintf(L"**** memory allocation failed\n");
            goto Cleanup;
        }

        if (ival_len > sizeof (rgbIV))
        {
            wprintf (L"**** block length is longer than the provided IV length\n");
            goto Cleanup;
        }

        // Allocate a buffer for the IV. The buffer is consumed during the 
        // encrypt/decrypt process.
       if(ival_len > 0)
       {    
         pbIV= (PBYTE) HeapAlloc (GetProcessHeap (), 0, ival_len + 1);
     if(NULL == pbIV)
     {
     wprintf(L"**** memory allocation failed\n");
     goto Cleanup;
     }

     strncpy(pbIV,rgbIV,ival_len);
     pbIV[ival_len]='\0';
       }
        if(!NT_SUCCESS(status = BCryptSetProperty(
                                    hAesAlg, 
                                    BCRYPT_CHAINING_MODE, 
                                    (PBYTE)BCRYPT_CHAIN_MODE_CBC, 
                                    sizeof(BCRYPT_CHAIN_MODE_CBC), 
                                    0)))
        {
            wprintf(L"**** Error 0x%x returned by BCryptSetProperty\n", status);
            goto Cleanup;
        }

              if(!NT_SUCCESS(status = BCryptGetProperty(
                                            hAesAlg, 
                                            BCRYPT_BLOCK_LENGTH, 
                                            (PBYTE)&cbBlockLen, 
                                            sizeof(DWORD), 
                                            &cbData, 
                                            0)))
        {
            wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
            goto Cleanup;
        }

         // Generate the key from supplied input key bytes.
        if(!NT_SUCCESS(status = BCryptGenerateSymmetricKey(
                                            hAesAlg, 
                                            &hKey, 
                                            pbKeyObject, 
                                            cbKeyObject, 
                                            (PBYTE)rgbAES256Key, 
                                            key_len, 
                                            0)))
        {
            wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n ", status);
     fprintf(stderr, "\nError number <%x>", GetLastError());
            goto Cleanup;
        }

        cbPlainText = sizeof(rgbPlaintext);
        pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);
        if(NULL == pbPlainText)
        {
            wprintf(L"**** memory allocation failed\n");
            goto Cleanup;
        }
        strncpy(pbPlainText, rgbPlaintext,cbPlainText);

     //Call BCryptEncrypt for pbPlainText1 and cipherText1


     //Call BCryptEncrypt for pbPlainText2 and cipherText2


     //Call BCryptEncrypt for pbPlainText2 and cipherText3

     //Combine cipherText1, cipherText2 and cipherText3 to cipherText variable

     //print cipherText in hex
   printf("\nCipher Text is :");
   for (DWORD i = 0; i < strlen(cipherText ); i++)
    {
        printf("%2.2X", cipherText[i]);
    }

    Cleanup:

        if(hAesAlg)
        {
            BCryptCloseAlgorithmProvider(hAesAlg,0);
        }

        if (hKey)    
        {
            BCryptDestroyKey(hKey);
        }

        if(pbCipherText)
        {
            HeapFree(GetProcessHeap(), 0, pbCipherText);
        }

        if(pbPlainText)
        {
            HeapFree(GetProcessHeap(), 0, pbPlainText);
        }

        if(pbKeyObject)
        {
            HeapFree(GetProcessHeap(), 0, pbKeyObject);
        }

        if(pbIV)
        {
            HeapFree(GetProcessHeap(), 0, pbIV);
        }

    }

I am expecting output "ABB7998EE4CE0699BD713AA9711E46F2EFCA1D16DABDA0291F1706B5F9F6B7B7E6705744981DB65EE6E14ECF896A90F8" for this code.

This is a sample program so I am using only 3 plain text buffers but in my real code the buffers can be multiple numbers.

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,514 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Drake Wu - MSFT 991 Reputation points
    2021-02-25T05:42:11.91+00:00

    Hi @Naren-3629 ,
    According to the documentation:

    If the input to encryption or decryption is scattered across multiple buffers, then you must chain calls to the BCryptEncrypt and BCryptDecrypt functions. Chaining is indicated by setting the BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG flag in the dwFlags member.

    This buffer must be supplied by the caller and must be at least as large as the maximum length of an authentication tag for the cipher you are using. To get the valid authentication tag lengths, use BCryptGetProperty to query the BCRYPT_AUTH_TAG_LENGTH property.

    You could check the sample on this thread.


    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.