Get MD5 Hash in Base 64 String (VC++)
Question
Monday, May 4, 2015 6:44 AM
Hi All,
I have a source code in C#, .NET where it converts a string into its equivalent encrypted Base 64 string.
The code is as follows:
string GetBase64MD5Hash(string text)
{
byte[] bytes = UnicodeEncoding.Unicode.GetBytes(text);
MD5CryptoServiceProvider hashProvider = new MD5CryptoServiceProvider();
byte[] hash = hashProvider.ComputeHash(inputBytes);
return Convert.ToBase64String(hash);
}
I need a write a method in C++ which does the same thing.
I found help in internet which considers the default Encoding format and normal encryption. But what i need here is Unicode Encoding and Base 64 format of the hash.
Please provide some help to solve this issue.
Thanks and Regards,
Kishor Reddy
All replies (14)
Monday, May 4, 2015 10:56 AM ✅Answered
So, if in C++ the text is a char* (or something from which you can easily obtain a char* such as std::string) then you need to use MultiByteToWideChar to convert the text to Unicode:
const char *text = "Example text";
// figure out how much memory we need for the converted string
int wcharCount = MultiByteToWideChar(CP_ACP, 0, text, strlen(text), nullptr, 0);
std::vector<wchar_t> wcharText(wcharCount);
// actually convert to Unicode
MultiByteToWideChar(CP_ACP, 0, text, strlen(text), &wcharText[0], wcharCount);
// Crypto functions usually work with BYTE* so get a BYTE* by casting
// and also get the length in bytes.
// These values can be passed to CryptHashData.
const BYTE *bytes = reinterpret_cast<const BYTE *>(&wcharText[0]);
DWORD byteLength = wcharText.size() * sizeof(wcharText[0]);
If instead of char* you have a wchar_t* you don't need to use MultiByteToWideChar, you need only the cast and the byte length computation:
const wchar_t *wtext = L"Example text";
const BYTE *bytes = reinterpret_cast<const BYTE *>(wtext);
DWORD byteLength = wcslen(wtext) * sizeof(wtext[0]);
For a CryptBinaryToString example let's say that we skip the hashing part and convert the bytes we obtained above to a base64 string:
DWORD base64Length = 0;
// Figure out how much memory we need for the base64 string
CryptBinaryToStringA(bytes, byteLength, CRYPT_STRING_BASE64, nullptr, &base64Length);
char *base64 = new char[base64Length];
// actually convert to base64
CryptBinaryToStringA(bytes, byteLength, CRYPT_STRING_BASE64, base64, &base64Length);
// display and delete the string for the sake of completness
puts(base64);
delete[] base64;
Monday, May 4, 2015 12:15 PM ✅Answered
Here you go:
HCRYPTPROV hProv;
HCRYPTHASH hHash;
// Create provider and hash algorithm
CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
// Hash the data
CryptHashData(hHash, bytes, byteLength, 0);
// Get back the hash value
BYTE hashBytes[128 / 8];
DWORD paramLength = sizeof(hashBytes);
CryptGetHashParam(hHash, HP_HASHVAL, hashBytes, ¶mLength, 0);
// Cleanup
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
// Convert hash to base64 string
CryptBinaryToStringA(hashBytes, sizeof(hashBytes), CRYPT_STRING_BASE64, nullptr, &base64Length);
...
Error handling has been omitted for simplicity. Also, because the MD5 hash algorithm always produces 128 bit hashes the code assumes this when retrieving the hash value. Normally the hash size is obtained by calling CryptGetHashParam with param = HP_HASHSIZE.
Monday, May 4, 2015 7:26 AM
"But what i need here is Unicode Encoding"
The C++ string may already be using Unicode and in such a case all you need to do is to cast the pointer to string to a BYTE*. If the string is an ASCII/ANSI string then you can convert it to Unicode by using MultiByteToWideChar.
"and Base 64 format of the hash"
You can use CryptBinaryToString with flags = CRYPT_STRING_BASE64 to obtain a base64 string from a byte array.
Monday, May 4, 2015 10:02 AM
Hi Mike,
Thanks for your reply. I tried but none if them succeeded.
Do you have any sample code available with you ?
Thanks and Regards.
Kishor Reddy
Monday, May 4, 2015 11:09 AM
Hi Mark,
On inserting the code you provided, i am getting the below linking error
Error 8 error LNK2019: unresolved external symbol __imp__CryptBinaryToStringA@20 referenced in function "public: long __thiscall MyClass::OnInitDialog(unsigned int,unsigned int,long,int &)" (?OnInitDialog@MyClass@@QAEJIIJAAH@Z)
Monday, May 4, 2015 11:43 AM
Hi Mark,
The below mentioned error is fixed. Now, just the Hash part is remaining.
Thanks for your reply....
Can u help me with the Hash part as well ?
Monday, May 4, 2015 1:00 PM
Hi Mike,
Your code works like a charm... Thanks a ton
Wednesday, May 6, 2015 12:52 PM
Hi Mike,
Your logic works absolutely fine for Normal strings. But if i enter Unicode strings (Hindi characters) as wchar_t*, different value are obtained in .NET and the above said implementation.
For example if we pass पगल्ग (Type "HINDI") in to .NET method, output hash string was "PYXd5fT3KQ+A63egMg6BBw=="
But if we pass the same string in hindi, we are getting the hash string as "nuoZqWfLJoJgtteHl/HEIg=="
i am following the steps you mentioned for wchar_t values.
Please have a look at my modified function.
char* CQuickSymAppView::GetMD5HashString(wchar_t* pcValue)
{
BYTE *bytes = reinterpret_cast<BYTE *>(&pcValue[0]);
DWORD byteLength = wcslen(pcValue) * sizeof(pcValue[0]);
HCRYPTPROV hCryptProvider;
HCRYPTHASH hCryptHash;
// Create provider and hash algorithm
CryptAcquireContext(&hCryptProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptCreateHash(hCryptProvider, CALG_MD5, 0, 0, &hCryptHash);
// Hash the data
CryptHashData(hCryptHash, bytes, byteLength, 0);
// Get back the hash value
BYTE hashBytes[128 / 8];
DWORD paramLength = sizeof(hashBytes);
CryptGetHashParam(hCryptHash, HP_HASHVAL, hashBytes, ¶mLength, 0);
// Cleanup
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProvider, 0);
DWORD base64Length = 0;
// Figure out how much memory we need for the base64 string
CryptBinaryToStringA(hashBytes, sizeof(hashBytes), CRYPT_STRING_BASE64, nullptr, &base64Length);
char *pcMD5HashString = new char[base64Length];
// Actually convert to base64
CryptBinaryToStringA(hashBytes, sizeof(hashBytes), CRYPT_STRING_BASE64, pcMD5HashString, &base64Length);
return pcMD5HashString;
}
Please let me know if i am missing anything
Few observation i have here are
- Every time the method is returning different values
- If i write the same code in another sample project (with Non-Unicode support), i am getting the correct value, always
Please suggest what could be done ?
Wednesday, May 6, 2015 2:56 PM
"For example if we pass पगल्ग (Type "HINDI") in to .NET method, output hash string was "PYXd5fT3KQ+A63egMg6BBw==""
I get the same hash when I call the C++ method like this:
char *md5 = GetMD5HashString(L"पगल्ग");
I get the same hash value in both Unicode and non-Unicode projects. That is expected because the code is written to not depend on the default character set.
So I suppose the question is now - where do you get that string? Did you put it in code like in my example or are you getting it from user input or a file?
Wednesday, May 6, 2015 4:55 PM
I am getting the string from user input.
My UI has one edit box. The value entered by the user is passed on to the method
Wednesday, May 6, 2015 5:14 PM
Can you show the code that you use to obtain a wchar_t* from the editbox?
Thursday, May 7, 2015 3:05 AM
I used the below code to create the Edit box control.
HWND editWnd = CreateWindowW(L"Edit", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, 17, 50, 145, 20, this->m_hWnd, NULL, GetModuleHandle(NULL), NULL);
HFONT hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(editWnd, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
SendMessage(editWnd, WM_SETFOCUS, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
For reading, i used the below code:
wchar_t buf[256];
GetWindowTextW(editWnd, buf, _countof(buf));
Thursday, May 7, 2015 4:42 AM
Well, your code looks OK and it works for me in Win32 project (both Unicode and non-Unicode builds). In general, I don't expect to see any different between Unicode and non-Unicode because you're using the W version of CreateWindow and GetWindowText.
The fact that you get a different value every time may indicate that some memory issues is going on (corruption, use of initialized or freed memory etc.) but I don't see anything like this in the posted code.
Thursday, May 7, 2015 5:19 PM
Hi Mike,
You r right. The value was read from the dialog which was destroyed before the value was retreived. This was fixed and everyting was working fine now.
Once again, thanks a ton