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 :
CryptAcquireContextA
defined to create a CSP
:
// create a cryptographic service provider (CSP)
CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)
For set key, I'm using of this way (import key):
CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey)
IV, Key, Plaintext sizes are :
#define DEFAULT_AES_KEY_SIZE 32
#define DEFAULT_IV_SIZE 16
#define BUFFER_FOR_PLAINTEXT 32
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
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:
The main (logical) error was that the buffer was not large enough to store the encrypted text (combined with a wrong value (0) for dwPlainSize - making the function to misleadingly succeed). According to [MS.Docs]: CryptEncrypt function:
If this parameter contains NULL, this function will calculate the required size for the ciphertext and place that in the value pointed to by the pdwDataLen parameter.
To find out the required size, make an additional call to the function, with pbData set to NULL (this practice is also encountered in other WinAPIs). Then allocate a buffer, fill it with required data, and make the "main" call the function on that buffer...
Added the missing #includes and main
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
CristiFati
answer is great, And it make me offer use of bellow statement for calculate cipher length :
CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBufSize, 0);
According Microsoft Doc :
If pbData is NULL, no error is returned, and the function stores the size of the encrypted data, in bytes, in the DWORD value pointed to by pdwDataLen) :
BOOL CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen
);
My Solution :
But in my code, I just forget to calculate szPlainText
size when i give it to CryptEncrypt()
:
DWORD dwPlainSize = 0; // initialized with 0
So "zero-length" has not any mean for bellow function (CryptEncrypt()
function always get a plain-text with 0
length) :
CryptEncrypt(hKey, NULL, TRUE, 0, szPlainText, &dwPlainSize, lstrlenA((LPCSTR)szPlainText) + 1)
And I should set its size with bellow statement (My code
will be worked just by adding this):
dwPlainSize = lstrlenA((LPCSTR)szPlainText);
Then pass it in right case :
CryptEncrypt(hKey, NULL, TRUE, 0, szPlainText, &dwPlainSize, BUFFER_FOR_PLAINTEXT)
So, output is like bellow :
Encrypted data : <É╙åh∩φ:bOPs r2w~w╪c╟D╡ï╥V╟neΓßv∩·J8cÅ╥
Size : 48
Decrypted data : abcdefghijklmnopqrstuvwxyzabcdefΓßv∩·J8cÅ╥
Size : 32
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With