I'm trying to use the winapi crypto api to get a base64 encoded hash. I copied and modified to get the function below. I got it from here mostly - https://msdn.microsoft.com/en-us/library/windows/desktop/aa382379(v=vs.85).aspx
Running it with calcHmacSha1("message", "key")
should give IIjfdNXyFGtIFGyvSWU3fp0L46Q=
. However it gives me SlLDwKvAoGBJ0atki7QFfj/181k=
, the non base64 version it gives is 4a 52 c3 c0 ab c0 a0 60 49 d1 ab 64 8b b4 05 7e 3f f5 f3 59
. This is a same situation encountered here - CryptoAPI returns incorrect result for HMAC_SHA1 - however his solution does not work for key's greater then 16 characters, and that is my need.
#pragma comment (lib, "Crypt32.lib")
#include <wincrypt.h>
std::string calcHmacSha1(std::string msg, std::string key) {
std::string hash;
std::vector<BYTE> msgbytebuffer(msg.begin(), msg.end());
std::vector<BYTE> keybytebuffer(key.begin(), key.end());
// http://msdn.microsoft.com/en-us/library/Aa379863
HCRYPTPROV hProv = NULL;
HCRYPTHASH hHash = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHmacHash = NULL;
PBYTE pbHash = NULL;
DWORD dwDataLen = 0;
BYTE* Data1 = &keybytebuffer[0]; // {0x6b,0x65,0x79};
BYTE* Data2 = &msgbytebuffer[0]; // {0x6D,0x65,0x73,0x73,0x61,0x67,0x65};
HMAC_INFO HmacInfo;
//--------------------------------------------------------------------
// Zero the HMAC_INFO structure and use the SHA1 algorithm for
// hashing.
debug_log("sizeof(Data2)", sizeof(Data2));
debug_log("sizeof(BYTE)", sizeof(BYTE));
ZeroMemory(&HmacInfo, sizeof(HmacInfo));
HmacInfo.HashAlgid = CALG_SHA1;
//--------------------------------------------------------------------
// Acquire a handle to the default RSA cryptographic service provider.
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
debug_log(" Error in AcquireContext 0x%08x", GetLastError());
goto ErrorExit;
}
//--------------------------------------------------------------------
// Derive a symmetric key from a hash object by performing the
// following steps:
// 1. Call CryptCreateHash to retrieve a handle to a hash object.
// 2. Call CryptHashData to add a text string (password) to the
// hash object.
// 3. Call CryptDeriveKey to create the symmetric key from the
// hashed password derived in step 2.
// You will use the key later to create an HMAC hash object.
if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) {
debug_log("Error in CryptCreateHash 0x%08x \n", GetLastError());
goto ErrorExit;
}
if (!CryptHashData(hHash, Data1, key.length() * sizeof(BYTE), 0)) {
debug_log("Error in CryptHashData 1 0x%08x", GetLastError());
goto ErrorExit;
}
if (!CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hKey)) {
debug_log("Error in CryptDeriveKey 0x%08x", GetLastError());
goto ErrorExit;
}
//--------------------------------------------------------------------
// Create an HMAC by performing the following steps:
// 1. Call CryptCreateHash to create a hash object and retrieve
// a handle to it.
// 2. Call CryptSetHashParam to set the instance of the HMAC_INFO
// structure into the hash object.
// 3. Call CryptHashData to compute a hash of the message.
// 4. Call CryptGetHashParam to retrieve the size, in bytes, of
// the hash.
// 5. Call malloc to allocate memory for the hash.
// 6. Call CryptGetHashParam again to retrieve the HMAC hash.
if (!CryptCreateHash(hProv, CALG_HMAC, hKey, 0, &hHmacHash)) {
debug_log("Error in CryptCreateHash key 0x%08x", GetLastError());
goto ErrorExit;
}
if (!CryptSetHashParam(hHmacHash, HP_HMAC_INFO, (BYTE*)&HmacInfo, 0)) {
debug_log("Error in CryptSetHashParam 0x%08x", GetLastError());
goto ErrorExit;
}
if (!CryptHashData(hHmacHash, Data2, msg.length() * sizeof(BYTE), 0)) {
debug_log("Error in CryptHashData 2 0x%08x", GetLastError());
goto ErrorExit;
}
//--------------------------------------------------------------------
// Call CryptGetHashParam twice. Call it the first time to retrieve
// the size, in bytes, of the hash. Allocate memory. Then call
// CryptGetHashParam again to retrieve the hash value.
if (!CryptGetHashParam(hHmacHash, HP_HASHVAL, NULL, &dwDataLen, 0)) {
debug_log("Error in CryptGetHashParam 0x%08x", GetLastError());
goto ErrorExit;
}
pbHash = (BYTE*)malloc(dwDataLen);
if(NULL == pbHash) {
debug_log("unable to allocate memory\n");
goto ErrorExit;
}
if (!CryptGetHashParam(hHmacHash, HP_HASHVAL, pbHash, &dwDataLen, 0)) {
debug_log("Error in CryptGetHashParam 0x%08x", GetLastError());
goto ErrorExit;
}
DWORD base64Size = 0;
if (!CryptBinaryToString(pbHash, dwDataLen, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &base64Size)) {
debug_log("Error in CryptBinaryToString 1 0x%08x", GetLastError());
goto ErrorExit;
};
WCHAR* base64 = new WCHAR[ base64Size + 1 ];
if (!CryptBinaryToString(pbHash, dwDataLen, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, base64, &base64Size)) {
debug_log("Error in CryptBinaryToString 2 0x%08x", GetLastError());
goto ErrorExit;
};
hash = string_cast<std::string>(base64);
delete[] base64;
debug_log("hash:", hash);
// printf("The hash is: ");
// char chash[512];
// for(DWORD i = 0 ; i < dwDataLen ; i++) {
// printf("%.2x ",pbHash[i]);
// }
MessageBox(NULL, L"hi", L"cap", 0);
// Free resources.
ErrorExit:
if(hHmacHash) CryptDestroyHash(hHmacHash);
if(hKey) CryptDestroyKey(hKey);
if(hHash) CryptDestroyHash(hHash);
if(hProv) CryptReleaseContext(hProv, 0);
if(pbHash) free(pbHash);
return hash;
}
the function work correct, not need fix it. but you not take to account what is key here used for
HMAC
- not direct your string key, but first, based on your string key,RC4
key created andHMAC
calculated for this binaryRC4
key.different keys -> different hash.
windows not let direct use string keys. it algorithm is better - first convert weak string key to more strong binary key. however if you want use string key and got result for it - can use code like this: