I have this code working to create a hash of the key to encrypt a string using Wincrypt:
wchar_t key[] = L"123456789AFA11";
wchar_t *key_str = key;
size_t len = lstrlenW(key_str);
DWORD dwStatus = 0;
BOOL bResult = FALSE;
wchar_t info[] = L"Microsoft Enhanced RSA and AES Cryptographic Provider";
HCRYPTPROV hProv;
if (!CryptAcquireContextW(&hProv, NULL, info, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
dwStatus = GetLastError();
printf("CryptAcquireContext failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { // Note that we will truncate the SHA265 hash to the first 128 bits because we are using AES128.
dwStatus = GetLastError();
printf("CryptCreateHash failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
if (!CryptHashData(hHash, (BYTE*)key_str, len * sizeof(wchar_t), 0)) {
DWORD err = GetLastError();
printf("CryptHashData Failed : %#x\n", err);
system("pause");
return (-1);
}
If I use char instead wchar as key the encrypted text is totally different since wchar is 2 bytes per character:
char key[] = "123456789AFA11";
char *key_str = key;
size_t len = lstrlenA(key_str);
DWORD dwStatus = 0;
BOOL bResult = FALSE;
wchar_t info[] = "Microsoft Enhanced RSA and AES Cryptographic Provider";
HCRYPTPROV hProv;
if (!CryptAcquireContextA(&hProv, NULL, info, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
dwStatus = GetLastError();
printf("CryptAcquireContext failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { // Note that we will truncate the SHA265 hash to the first 128 bits because we are using AES128.
dwStatus = GetLastError();
printf("CryptCreateHash failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
if (!CryptHashData(hHash, (BYTE*)key_str, len, 0)) {
DWORD err = GetLastError();
printf("CryptHashData Failed : %#x\n", err);
system("pause");
return (-1);
}
My question is which should I use to hash the key, char string or wchar_t string?
Also another question is UTF-8 in my apps means to use always char and UTF-16 means the use of wchar_t? I use always UNICODE in Visual Studio 2017 then should I use wchar since WindowsAPI's outputs seems to be wchar_t?
which should I use to hash the key, char string or wchar_t string?
That is a matter of personal choice. Use whichever one suits your needs. Encryption operates on raw bytes, it doesn't care what those bytes represent.
UTF-8 in my apps means to use always char and UTF-16 means the use of wchar_t?
On windows, yes. wchar_t is not 2 bytes on most other platforms, so not UTF-16.
I use always UNICODE in Visual Studio 2017 then should I use wchar since WindowsAPI's outputs seems to be wchar_t?
Yes, Windows is a Unicode-based OS, and most of its string-based APIs expect/return UTF-16. But encryption APIs do not care about that. Though, in your case, you should probably consider converting UTF-16 to UTF-8 before encrypting, and then convert UTF-8 to UTF-16 after decrypting. That way, your encrypted data takes up less storage space, at least.
Related
const char* Launcher::GetProjectName()
{
PVOID data;
LPDWORD pcbData;
HKEY OpenResult;
LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Environment", NULL, KEY_READ, &OpenResult);
if (status != ERROR_SUCCESS)
{
LOG(ERROR) << "Could not found registry key 'Environment'";
}
else
{
LPCWSTR project_name_key = L"project_name";
DWORD data_type;
WCHAR value[255];
PVOID pvData = value;
DWORD size = sizeof(value);
status = RegGetValue(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, pvData, &size);
if (status != ERROR_SUCCESS)
{
LOG(ERROR) << "Could not found registry value 'project_name'";
}
else
{
switch (data_type)
{
case REG_DWORD:
wprintf(L"Value data: %x\n", *(DWORD*)pvData);
break;
case REG_SZ:
wprintf(L"Value data: %s\n", (PWSTR)pvData);
}
}
RegCloseKey(OpenResult);
}
return 0;
}
I'm trying to get this registry key I made named "project_name" and return it as a char* or a std::string. However, I'm getting garbage data in pvData. What am I doing wrong here? I've seen some other stackoverflow posts and tried to replicate their setup as well but nothing is working. My entire goal here is to retrieve an environment variable using the windows registry.
I think there's a mismatch of ANSI and Unicode expectations. Your code is likely compiling for ANSI, but you're passing a wide-char buffer. Let's just explicitly call the A version of the Registry functions so you can stay in the ANSI string space.
Instead of this:
WCHAR value[255];
PVOID pvData = value;
DWORD size = sizeof(value);
status = RegGetValue(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, pvData, &size);
Use this:
char value[255];
DWORD size = sizeof(value);
status = RegGetValueA(OpenResult, NULL, project_name_key, RRF_RT_ANY, &data_type, value, &size);
Then, return a std::string as follows:
Declare your function to return a string, not a pointer:
const std::string Launcher::GetProjectName()
Then simply return value as a string;
return std::string(value);
I am writing a simple C++ program to grab a Windows registry value on a 64 bit machine.
The problem is it only works for about 50% of the registry's and for the other half "ret" does not return ERROR_SUCCESS(0).
My question is to why I am getting these inconsistent returns and also when I attempt to make the path longer than two directories it also break.
ex.
keypath = TEXT("SOFTWARE\\Perl\\ASDF");
Here is my code.
LPCTSTR keypath = TEXT("SOFTWARE\\Perl");
HKEY key = NULL;
LONG ret = ERROR_SUCCESS;
DWORD BufferSize = TOTALBYTES;
DWORD cbData;
DWORD dwRet;
DWORD type;
char registry[256] = {'\0'};
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath, 0, KEY_QUERY_VALUE, &key);
PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) malloc( BufferSize );
cbData = BufferSize;
if (ret == ERROR_SUCCESS)
{
dwRet = RegQueryValueEx( key,
TEXT("BinDir"),
NULL,
&type,
(LPBYTE) PerfData,
&cbData );
RegCloseKey(key);
printf("\nFinal buffer size is %d\n", BufferSize);
int i = 0;
while ((*PerfData).Signature[i] != NULL)
{
registry[i] = (char)(*PerfData).Signature[i];
i++;
}
printf("registery: %s\n", registry);
}
editing in fixes.
I'm trying to decrypt - using the microsoft's CryptoAPI in C++ - a short message encrypted using mcrypt_encrypt in PHP. The php line is:
mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $key, $msg, MCRYPT_MODE_CBC);
where $key and $msg are strings.
In C++ I have the key, and my decryption function looks like this:
bool decrypt( const unsigned char* input_buffer,
const size_t& input_size,
const unsigned char* key,
const size_t& key_size,
unsigned char* output_buffer,
DWORD* out_size)
{
Log(L"START init_crypto");
bool ret = false;
HCRYPTKEY hKey = NULL;
HCRYPTPROV hCryptProv = NULL;
DWORD dwDataLen = 0;
// Attempt to acquire a handle to the crypto provider for AES
if(0 == CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) ){//PROV_RSA_AES
Log(L"CryptAcquireContext failed with code %ld", GetLastError());
goto end;
}
// key creation based on
// http://mirror.leaseweb.com/NetBSD/NetBSD-release-5-0/src/dist/wpa/src/crypto/crypto_cryptoapi.c
struct {
BLOBHEADER hdr;
DWORD len;
BYTE key[32];
} key_blob;
key_blob.hdr.bType = PLAINTEXTKEYBLOB;
key_blob.hdr.bVersion = CUR_BLOB_VERSION;
key_blob.hdr.reserved = 0;
key_blob.hdr.aiKeyAlg = CALG_AES_256;
key_blob.len = 32;//key_size;
memset(key_blob.key, '\0', sizeof(key_blob.key));
assert(key_size <= sizeof(key_blob.key));
CopyMemory(key_blob.key, key, min(key_size, sizeof(key_blob.key)));
if (!CryptImportKey( hCryptProv,
(BYTE *)&key_blob,
sizeof(key_blob),
0,
CRYPT_EXPORTABLE,
&hKey)){
Log(L"Error in CryptImportKey 0x%08x \n", GetLastError());
goto free_context;
}
else{
//---------------------------
// Set Mode
DWORD dwMode = CRYPT_MODE_CBC;
if(!CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 )){
// Handle error
Log(L"Cannot set the cbc mode for decryption 0x%08x \n", GetLastError());
goto free_key;
}
//----------------------------
// Set IV
DWORD dwBlockLen = 0;
DWORD dwDataLen = sizeof(dwBlockLen);
if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)){
// Handle error
Log(_USTR("Cannot get the block length 0x%08x \n"), GetLastError());
goto free_key;
}
dwBlockLen /= 8;
BYTE *pbTemp = NULL;
if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))){
// Handle error
Log(L"Cannot allcoate the IV block 0x%08x \n", GetLastError());
goto free_key;
}
memset(pbTemp, '\0', dwBlockLen);
if (!CryptSetKeyParam(hKey, KP_IV, pbTemp, 0)){
// Handle error
Log(L"Cannot set the IV block 0x%08x \n", GetLastError());
LocalFree(pbTemp);
goto free_key;
}
LocalFree(pbTemp);
}
CopyMemory(output_buffer, input_buffer, min(*out_size, input_size));
*out_size = input_size;
if (!CryptDecrypt(hKey, NULL, TRUE, 0, output_buffer, out_size)){
Log(L"CryptDecrypt failed with code %ld", GetLastError());
goto free_key;
}
else{
Log(L"Decryption...");
ret = true;
}
free_key:
if (hKey)
CryptDestroyKey( hKey );
free_context:
if (hCryptProv)
CryptReleaseContext(hCryptProv, 0);
end:
return ret;
}
I consistently get the "bad data" error at CryptDecrypt()... I may be missing something obvious - if so, please help.
EDIT - To isolate the cause of the problem I replaced the two lines before CryptDecrypt (the CopyMemory stuff) with the following code:
....
strcpy((char*)output_buffer, "stuff");
DWORD plain_txt_len = strlen((char*)output_buffer);
if (!CryptEncrypt(hKey, NULL, TRUE, 0, output_buffer, &plain_txt_len, *out_size)){
Log(L"CryptEncrypt failed with code 0x%08x", GetLastError());
goto free_key;
}
...
and the CryptDecrypt is working -- which makes me believe that the problem is the key/and or message transmission from php to C++ ... If you agree can you give me a hint on how to make sure that the strings I use in PHP are the same with the ones in C++ (at byte level?)
EDIT2 --
After I changed my strings in binary streams (using pack) in php, and after I implemented the workaround(?) for AES vs Rijndael from here : http://kix.in/2008/07/22/aes-256-using-php-mcrypt/ I finaly have CryptDecrypt decrypting my PHP message... the problem is that it also still fails - even if the output contains the decrypted text. Any ideas about why could this happen?
Try passing NULL instead of CRYPT_VERIFYCONTEXT when acquiring the context.
With block encryption algorithms such as AES you need to add padding to the data being encrypted up to a multiple of the block length. Using your code example that already retrieves the block size you can calculate the padding required for encryption:
dwPadding = dwBlockLen - dwDataLen % dwBlockLen
Then append "dwPadding" number of characters (NULL works fine) to the data being encrypted and you will no longer get "Bad Data" errors in decryption.
You can also find out the size of the required encryption buffer directly by making an additional call to "CryptEncrypt" with a NULL buffer before the actual encryption:
dwBuffSize = dwDataLen
CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBuffSize, 0)
dwPadding = dwBuffsize - dwDataLen
Both methods are equivalent and produce the same desired result. The "CryptDecrypt" function already knows about padding and will return the actual size of the decryption buffer in one go so you will know exactly where your decrypted data ends.
I'm casting (char * ) on data and i'm getting only one char value in the registry. if
i don't use the casting msvc 2010 tells me that the argument type LPCTSTR is incompatible with const char *.
can someone help me?
HKEY hKey;
LPCTSTR sk = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS , &hKey);
if (openRes==ERROR_SUCCESS) {
printf("Success opening key.");
} else {
printf("Error opening key.");
}
LPCTSTR value = TEXT("SomeKey");
LPCTSTR data = L"TestData\0";
LONG setRes = RegSetValueEx (hKey, value, 0, REG_SZ, (LPBYTE)data, strlen(data)+1);
if (setRes == ERROR_SUCCESS) {
printf("Success writing to Registry.");
} else {
printf("Error writing to Registry.");
}
cout << setRes << endl;
LONG closeOut = RegCloseKey(hKey);
if (closeOut == ERROR_SUCCESS) {
printf("Success closing key.");
} else {
printf("Error closing key.");
}
strlen(data) is probably returning a value of 1, as strlen expects a char* and L"TestData\0" is wide. Use TEXT("TestData\0") and call _tcslen(data).
Note that RegSetValueEx expects the sizeof the data, so use _tcslen(data) * sizeof(TCHAR)
Where are you casting data?
Either way, it looks like you may be working with wide characters, but you seem to be using "plain old" strlen - instead of wcslen or some other function intended to work with wide-character strings.
replace the L"TestData" by _T("TestData"); and strlen(data)+1 by tcslen(data) * sizeof(TCHAR));
so your code looks like this :
LPCTSTR value = TEXT("SomeKey");
LPCTSTR data = TEXT("TestData");
LONG setRes = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, _tcslen(data) * sizeof(TCHAR));
I've created this program that can encrypt a found file and then it can be decrypted later via the CryptDecrypt function. The function succeeds but instead of decrypting the file back to plain text it makes the file look even more encrypted.
I've put both the CryptEncrypt function and CryptDecrypt function so you can have a more less view what I'm doing wrong. One more thing I'm using the Win32 API, no MFC or ATL.
if (LOWORD(wParam) == WORD(decrypt_id))
{
wchar_t filepath[256];
GetWindowTextW(hWnd, filepath, (int)256);
_wstat(filepath, &info4);
const long bytesize = info4.st_size;
unsigned char *buffer = new unsigned char[bytesize];
file = _wfopen(filepath, L"r");
size_t readsize = fread(buffer, sizeof(char), info4.st_size , file);
BOOL returnn = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
BOOL rvalue1 = CryptGenKey(hCryptProv, CALG_RC4, KEYLENGTH | CRYPT_EXPORTABLE, &hkey);
DWORD val = GetLastError();
DWORD datalength = info4.st_size;
BOOL rvalue3 = CryptDecrypt(hkey, NULL, FALSE, NULL, buffer, &datalength);
file2 = _wfopen(filepath, L"w");
size_t writesize = fwrite(buffer, sizeof(char), sizeof(buffer), file2);
free(buffer);
CryptReleaseContext(hCryptProv, 0);
CryptDestroyKey(hkey);
if (rvalue3 == 0)
{
DWORD result = GetLastError();
wchar_t dest[256] = L"Decryptor Failed To Decrypt File!";
wcscat_s(dest, L"\n");
wcscat_s(dest, L"Error Code: ");
wchar_t code[256];
swprintf_s(code, L"%d", result);
wcscat_s(dest, code);
wcscat_s(dest, L"\n");
wcscat_s(dest, L"Error Code Information at: http://msdn.microsoft.com/en-us/library/ms681381(v=VS.85).aspx");
MessageBoxW(hWnd, dest, L"Error", MB_ICONERROR | MB_OK);
ShowWindow(encrypt_button, SW_HIDE);
}
else
{
MessageBox(hWnd, L"Successfully Decrypted The File!", L"", MB_OK | MB_ICONINFORMATION);
ShowWindow(encrypt_button, SW_HIDE);
}
}
if (LOWORD(wParam) == WORD(encrypt_id))
{
wchar_t filepath[256];
GetWindowTextW(hWnd, filepath, (int)256);
_wstat(filepath, &info4);
const long bytesize = info4.st_size;
unsigned char *buffer = new unsigned char[bytesize];
file = _wfopen(filepath, L"r");
size_t readsize = fread(buffer, sizeof(char), info4.st_size , file);
BOOL returnn = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
BOOL rvalue1 = CryptGenKey(hCryptProv, CALG_RC4, KEYLENGTH | CRYPT_EXPORTABLE, &hkey);
DWORD val = GetLastError();
DWORD datalength = info4.st_size;
BOOL rvalue3 = CryptEncrypt(hkey, NULL, FALSE, NULL, buffer, &datalength, datalength);
file2 = _wfopen(filepath, L"w");
size_t writesize = fwrite(buffer, sizeof(char), sizeof(buffer), file2);
free(buffer);
CryptDestroyKey(hkey);
CryptReleaseContext(hCryptProv, 0);
if (rvalue3 == 0)
{
DWORD result = GetLastError();
wchar_t dest[256] = L"Encryptor Failed To Encrypt File!";
wcscat_s(dest, L"\n");
wcscat_s(dest, L"Error Code: ");
wchar_t code[256];
swprintf_s(code, L"%d", result);
wcscat_s(dest, code);
wcscat_s(dest, L"\n");
wcscat_s(dest, L"Error Code Information at: http://msdn.microsoft.com/en-us/library/ms681381(v=VS.85).aspx");
MessageBoxW(hWnd, dest, L"Error", MB_ICONERROR | MB_OK);
ShowWindow(encrypt_button, SW_HIDE);
}
else
{
MessageBox(hWnd, L"Successfully Encrypted The File!", L"", MB_OK | MB_ICONINFORMATION);
ShowWindow(encrypt_button, SW_HIDE);
}
}
It looks like before encrypting or decrypting, you're generating a random key with CryptGenKey. This means that you will use a different key for encryption and decryption, so your file will not decrypt correctly.
You will need to use the same key for encryption or decryption. Either by exporting and importing the key, or possibly by using CryptDeriveKey to derive the key from a shared password.