cwinapiencryptionaeswincrypt

WinAPI - CryptDecrypt() not working properly in AES 256


I used to work with crypto++ in Visual Studio before, but now I want to use of wincrypt.h API functions to encrypt a string with AES 256 with an IV (cbc mode).

I did bellow steps but I'm confused about CryptEncrypt() and CryptDecrypt() functions, because It seems they don't work properly :

This is my whole code :

// handles for csp and key
HCRYPTPROV hProv = NULL;
HCRYPTPROV hKey = NULL;
BYTE szKey[DEFAULT_AES_KEY_SIZE + 1] = {0};
BYTE szIV[DEFAULT_IV_SIZE + 1] = {0};
// plain bytes
BYTE szPlainText[BUFFER_FOR_PLAINTEXT + 1] = {0};
DWORD dwPlainSize = 0;

// initalize key and plaintext
StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
StrCpyA((LPSTR)szIV, "4455667788990011");
StrCpyA((LPSTR)szPlainText, "abcdefghijklmnopqrstuvwxyzabcdef");

// blob data for CryptImportKey() function (include key and version and so on...)
struct AES256KEYBLOB
{
    AES256KEYBLOB() { StrCpyA((LPSTR)szBytes, 0); }
    BLOBHEADER bhHdr;
    DWORD dwKeySize;
    BYTE szBytes[DEFAULT_AES_KEY_SIZE + 1];
} AESBlob;

AESBlob.bhHdr.bType = PLAINTEXTKEYBLOB;
AESBlob.bhHdr.bVersion = CUR_BLOB_VERSION;
AESBlob.bhHdr.reserved = 0;
AESBlob.bhHdr.aiKeyAlg = CALG_AES_256;
AESBlob.dwKeySize = DEFAULT_AES_KEY_SIZE;
StrCpyA((LPSTR)AESBlob.szBytes, (LPCSTR)szKey);

// create a cryptographic service provider (CSP)
if(CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
{
    if(CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey))
    {
        if(CryptSetKeyParam(hKey, KP_IV, szIV, 0))
        {
            if(CryptEncrypt(hKey, NULL, TRUE, 0, szPlainText, &dwPlainSize, lstrlenA((LPCSTR)szPlainText) + 1))
            {
                printf("\nEncrypted data : %s\nSize : %d\n", (LPCSTR)szPlainText, dwPlainSize);

                if(CryptDecrypt(hKey, NULL, TRUE, 0, szPlainText, &dwPlainSize)) {
                    printf("\nDecrypted data : %s\nSize : %d\n", (LPCSTR)szPlainText, dwPlainSize);
                }
                else
                    printf("failed to decrypt!");
            }
            else
                printf("failed to encrypt");
        }
    }
}

It just encrypt half section of plaintext And decrypting not done! even By changing just szPlainText value, it always give me bellow output (It means that CryptEncrypt() and CryptDecrypt() does not working as expected!) :

Encrypted data : U╡π7ÑL|FΩ$}├rUqrstuvwxyzabcdef
Size : 16

Decrypted data : U╡π7ÑL|FΩ$}├rUqrstuvwxyzabcdef
Size : 0

Solution

  • Here's a variant, that I'm running from VStudio 2015.

    code.c:

    #include <windows.h>
    #include <wincrypt.h>
    #include <stdio.h>
    #include <Shlwapi.h>
    
    #define DEFAULT_AES_KEY_SIZE 32
    #define DEFAULT_IV_SIZE 16
    #define BUFFER_FOR_PLAINTEXT 32
    
    #define CLEANUP_CRYPT_STUFF(PROV, KEY) \
        CryptDestroyKey(KEY); \
        CryptReleaseContext(PROV, 0)
    
    #define PRINT_FUNC_ERR_AND_RETURN(FUNC) \
        printf("%s (line %d) failed: %d\n", ##FUNC, __LINE__, GetLastError()); \
        return -1
    
    
    typedef struct AES256KEYBLOB_ {
        BLOBHEADER bhHdr;
        DWORD dwKeySize;
        BYTE szBytes[DEFAULT_AES_KEY_SIZE + 1];
    } AES256KEYBLOB;
    
    
    int main() {
        // handles for csp and key
        HCRYPTPROV hProv = NULL;
        HCRYPTKEY hKey = NULL;
        BYTE szKey[DEFAULT_AES_KEY_SIZE + 1] = { 0 };
        BYTE szIV[DEFAULT_IV_SIZE + 1] = { 0 };
        // plain bytes
        BYTE szPlainText[BUFFER_FOR_PLAINTEXT + 1] = { 0 }, *pBuf = NULL;
        AES256KEYBLOB AESBlob;
        memset(&AESBlob, 0, sizeof(AESBlob));
    
        // initalize key and plaintext
        StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
        StrCpyA((LPSTR)szIV, "4455667788990011");
        StrCpyA((LPSTR)szPlainText, "abcdefghijklmnopqrstuvwxyzabcdef");
        DWORD dwPlainSize = lstrlenA((LPCSTR)szPlainText), dwBufSize = dwPlainSize, dwBufSize2 = dwPlainSize;
    
        // blob data for CryptImportKey() function (include key and version and so on...)
        AESBlob.bhHdr.bType = PLAINTEXTKEYBLOB;
        AESBlob.bhHdr.bVersion = CUR_BLOB_VERSION;
        AESBlob.bhHdr.reserved = 0;
        AESBlob.bhHdr.aiKeyAlg = CALG_AES_256;
        AESBlob.dwKeySize = DEFAULT_AES_KEY_SIZE;
        StrCpyA((LPSTR)AESBlob.szBytes, (LPCSTR)szKey);
    
        // create a cryptographic service provider (CSP)
        if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
            PRINT_FUNC_ERR_AND_RETURN(CryptAcquireContextA);
        }
        if (!CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey)) {
            CryptReleaseContext(hProv, 0);
            PRINT_FUNC_ERR_AND_RETURN(CryptImportKey);
        }
        if (!CryptSetKeyParam(hKey, KP_IV, szIV, 0)) {
            CLEANUP_CRYPT_STUFF(hProv, hKey);
            PRINT_FUNC_ERR_AND_RETURN(CryptSetKeyParam);
        }
        if (CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBufSize, 0)) {
            printf("%d bytes required to hold the encrypted buf\n", dwBufSize);
            if ((pBuf = calloc(dwBufSize, sizeof(BYTE))) == NULL)
            {
                CLEANUP_CRYPT_STUFF(hProv, hKey);
                PRINT_FUNC_ERR_AND_RETURN(calloc);
            }
            StrCpyA(pBuf, szPlainText);
        } else {
            CLEANUP_CRYPT_STUFF(hProv, hKey);
            PRINT_FUNC_ERR_AND_RETURN(CryptEncrypt);
        }
        if (CryptEncrypt(hKey, NULL, TRUE, 0, pBuf, &dwBufSize2, dwBufSize)) {
            printf("\nEncrypted data: [%s]\nSize: %d\n", (LPCSTR)pBuf, dwBufSize2);
            if (CryptDecrypt(hKey, NULL, TRUE, 0, pBuf, &dwBufSize)) {
                printf("\nDecrypted data: [%s]\nSize: %d\n", (LPCSTR)pBuf, dwBufSize);
            } else {
                free(pBuf);
                CLEANUP_CRYPT_STUFF(hProv, hKey);
                PRINT_FUNC_ERR_AND_RETURN(CryptDecrypt);
            }
        } else {
            free(pBuf);
            CLEANUP_CRYPT_STUFF(hProv, hKey);
            PRINT_FUNC_ERR_AND_RETURN(CryptEncrypt);
        }
        free(pBuf);
        CLEANUP_CRYPT_STUFF(hProv, hKey);
        return 0;
    }
    

    Notes:

    Output:

    48 bytes required to hold the encrypted buf
    
    Encrypted data: [<É╙åh∩φ:bOPs  r2w~w╪c╟D╡ï╥V╟neΓßv∩·J8cÅ╥²²²²s]
    Size: 48
    
    Decrypted data: [abcdefghijklmnopqrstuvwxyzabcdefΓßv∩·J8cÅ╥²²²²s]
    Size: 32