Search code examples
copensslaescryptoapi

AES128 with CryptoAPI


I'm trying to encrypt strings in AES128 using CryptoAPI, but I'm not getting it for some reason. When I compile, it simply crashes the application. I believe the problem is in the calls of the CryptDecrypt and CryptEncrypt functions, as I wrote this function based on another that I found on the internet. I know nothing concrete about it, nor the way to use it.

Here is my code:

#include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <cmath>
#include <string>

#pragma comment(lib, "crypt32.lib")

#define BLOCK_LEN 16

std::string AES128(std::string key, std::string data, bool enc)
{
    bool Result = false;
    size_t blocks = ceil((float)data.length() / BLOCK_LEN) + 1;
    BYTE* chunk = new BYTE[blocks * BLOCK_LEN];
    memset(chunk, 0, blocks * BLOCK_LEN);
    memcpy(chunk, data.c_str(), data.length());

    HCRYPTPROV hProv;
    if (!CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
        goto finally;

    HCRYPTHASH hHash;
    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
        goto finally;

    if (!CryptHashData(hHash, (BYTE*)key.c_str(), key.length(), 0))
        goto finally;

    HCRYPTKEY hKey;
    if (!CryptDeriveKey(hProv, CALG_AES_128, hHash, 0, &hKey))
        goto finally;

    for (int i = 0; i < blocks; i++)
        switch (enc)
        {
        case true:
        {
            DWORD out_len = BLOCK_LEN;
            if (!CryptEncrypt(hKey, NULL, i + 1 == blocks, NULL, &chunk[i * BLOCK_LEN], &out_len, blocks * BLOCK_LEN))
                goto finally;
            break;
        }

        case false:
        {
            DWORD out_len = BLOCK_LEN;
            if (!CryptDecrypt(hKey, NULL, i + 1 == blocks, NULL, &chunk[i * BLOCK_LEN], &out_len))
                goto finally;
            break;
        }
        }

    Result = true;
    goto finally;

    finally:
    {
        if (hProv)
            CryptReleaseContext(hProv, 0);
        if (hHash)
            CryptDestroyHash(hHash);
        if (hKey)
            CryptDestroyKey(hKey);

        if (Result)
            return std::string(reinterpret_cast<char*>(chunk));
        else
            return "";
    }
}

int main()
{
    std::string key = "12345";
    std::string data = "aaaaaabbbbbb";
    std::string encdata = AES128(key, data, true);
    std::string decdata = AES128(key, encdata, false);

    printf("%s => %s => %s", data.c_str(), encdata.c_str(), decdata.c_str());
    system("pause");
}

Sorry bad English, I'm Brazilian.


Solution

  • Your calculations are off: ceil((float)data.length() / BLOCK_LEN) + 1 is 2 for a 12-byte input.

    But you don't need to encrypt in chunks, the crypt API can handle chunking for you. Just call it once for the entire input.

    Here's a modified version that works:

    #include <Windows.h>
    #include <wincrypt.h>
    #include <stdio.h>
    #include <cmath>
    #include <string>
    
    #pragma comment(lib, "crypt32.lib")
    
    #define BLOCK_LEN 16
    
    std::string AES128(const std::string& key, const std::string& data, bool enc)
    {
        std::string result = data;
        bool Result = false;
        HCRYPTPROV hProv = NULL;
        HCRYPTHASH hHash = NULL;
        HCRYPTKEY hKey = NULL;
        result.resize((data.length() + BLOCK_LEN - 1) & ~(BLOCK_LEN - 1));
        DWORD out_len = data.length();
    
        do {
            if (!CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
                break;
    
            if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
                break;
    
            if (!CryptHashData(hHash, (const BYTE*)key.c_str(), key.length(), 0))
                break;
    
            if (!CryptDeriveKey(hProv, CALG_AES_128, hHash, 0, &hKey))
                break;
    
            if (enc)
            {
                if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)result.data(), &out_len, result.length()))
                    break;
            }
            else
            {
                if (!CryptDecrypt(hKey, NULL, TRUE, 0, (BYTE*)result.data(), &out_len))
                    break;
            }
    
            result.resize(out_len);
            Result = true;
        } while (false);
    
        if (hKey)
            CryptDestroyKey(hKey);
        if (hHash)
            CryptDestroyHash(hHash);
        if (hProv)
            CryptReleaseContext(hProv, 0);
    
        if (!Result)
            result = "";
        return result;
    }
    
    int main()
    {
        std::string key = "12345";
        std::string data = "aaaaaabbbbbb";
        std::string encdata = AES128(key, data, true);
        std::string decdata = AES128(key, encdata, false);
    
        printf("%s => %s => %s\n", data.c_str(), encdata.c_str(), decdata.c_str());
    }