I'm trying to replicate PHP's hash_hmac function in C++ using wincrypt. Someone sent me this MS link titled "Example C Program: Creating an HMAC". I copy all of the code and the result prints out
The hash is: 48 f2 57 38 29 29 43 16 fd f4 db 58 31 e1 0c 74 48 8e d4 e2
The sample data in this case uses "password" and "message". So now I switch over to the PHP function and I call it two ways to make sure the order is right:
echo(hash_hmac('sha1', "password", "message", false));
echo PHP_EOL;
echo(hash_hmac('sha1', "message", "password", false));
And this prints out
f5959d1d5c133fd9368218e1ffde3075382d7fd7
4c2de361ba8958558de3d049ed1fb5c115656e65
So none of the hashes are matching. What is the difference here? How do I get the C++ one to match the PHP?
I'm being forced to edit this question to explain why this question doesn't help me. The two answers are "use openssl" and "it's impossible". Firstly, openssl isn't an option. Second, it IS possible. It has to be, because PHP is written in C++ as are other third party libraries that do it...
Full code below in case the MS page dies someday.
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
int main()
{
//--------------------------------------------------------------------
// Declare variables.
//
// hProv: Handle to a cryptographic service provider (CSP).
// This example retrieves the default provider for
// the PROV_RSA_FULL provider type.
// hHash: Handle to the hash object needed to create a hash.
// hKey: Handle to a symmetric key. This example creates a
// key for the RC4 algorithm.
// hHmacHash: Handle to an HMAC hash.
// pbHash: Pointer to the hash.
// dwDataLen: Length, in bytes, of the hash.
// Data1: Password string used to create a symmetric key.
// Data2: Message string to be hashed.
// HmacInfo: Instance of an HMAC_INFO structure that contains
// information about the HMAC hash.
//
HCRYPTPROV hProv = NULL;
HCRYPTHASH hHash = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHmacHash = NULL;
PBYTE pbHash = NULL;
DWORD dwDataLen = 0;
BYTE Data1[] = {0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64};
BYTE Data2[] = {0x6D,0x65,0x73,0x73,0x61,0x67,0x65};
HMAC_INFO HmacInfo;
//--------------------------------------------------------------------
// Zero the HMAC_INFO structure and use the SHA1 algorithm for
// hashing.
ZeroMemory(&HmacInfo, sizeof(HmacInfo));
HmacInfo.HashAlgid = CALG_SHA1;
//--------------------------------------------------------------------
// Acquire a handle to the default RSA cryptographic service provider.
if (!CryptAcquireContext(
&hProv, // handle of the CSP
NULL, // key container name
NULL, // CSP name
PROV_RSA_FULL, // provider type
CRYPT_VERIFYCONTEXT)) // no key access is requested
{
printf(" Error in AcquireContext 0x%08x \n",
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, // handle of the CSP
CALG_SHA1, // hash algorithm to use
0, // hash key
0, // reserved
&hHash)) // address of hash object handle
{
printf("Error in CryptCreateHash 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptHashData(
hHash, // handle of the hash object
Data1, // password to hash
sizeof(Data1), // number of bytes of data to add
0)) // flags
{
printf("Error in CryptHashData 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptDeriveKey(
hProv, // handle of the CSP
CALG_RC4, // algorithm ID
hHash, // handle to the hash object
0, // flags
&hKey)) // address of the key handle
{
printf("Error in CryptDeriveKey 0x%08x \n",
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, // handle of the CSP.
CALG_HMAC, // HMAC hash algorithm ID
hKey, // key for the hash (see above)
0, // reserved
&hHmacHash)) // address of the hash handle
{
printf("Error in CryptCreateHash 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptSetHashParam(
hHmacHash, // handle of the HMAC hash object
HP_HMAC_INFO, // setting an HMAC_INFO object
(BYTE*)&HmacInfo, // the HMAC_INFO object
0)) // reserved
{
printf("Error in CryptSetHashParam 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptHashData(
hHmacHash, // handle of the HMAC hash object
Data2, // message to hash
sizeof(Data2), // number of bytes of data to add
0)) // flags
{
printf("Error in CryptHashData 0x%08x \n",
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, // handle of the HMAC hash object
HP_HASHVAL, // query on the hash value
NULL, // filled on second call
&dwDataLen, // length, in bytes, of the hash
0))
{
printf("Error in CryptGetHashParam 0x%08x \n",
GetLastError());
goto ErrorExit;
}
pbHash = (BYTE*)malloc(dwDataLen);
if(NULL == pbHash)
{
printf("unable to allocate memory\n");
goto ErrorExit;
}
if (!CryptGetHashParam(
hHmacHash, // handle of the HMAC hash object
HP_HASHVAL, // query on the hash value
pbHash, // pointer to the HMAC hash value
&dwDataLen, // length, in bytes, of the hash
0))
{
printf("Error in CryptGetHashParam 0x%08x \n", GetLastError());
goto ErrorExit;
}
// Print the hash to the console.
printf("The hash is: ");
for(DWORD i = 0 ; i < dwDataLen ; i++)
{
printf("%2.2x ",pbHash[i]);
}
printf("\n");
// 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 0;
}
Use plaintext key, something like this:
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
int main()
{
//--------------------------------------------------------------------
// Declare variables.
//
// hProv: Handle to a cryptographic service provider (CSP).
// This example retrieves the default provider for
// the PROV_RSA_FULL provider type.
// hHash: Handle to the hash object needed to create a hash.
// hKey: Handle to a symmetric key. This example creates a
// key for the RC4 algorithm.
// hHmacHash: Handle to an HMAC hash.
// pbHash: Pointer to the hash.
// dwDataLen: Length, in bytes, of the hash.
// Data1: Password string used to create a symmetric key.
// Data2: Message string to be hashed.
// HmacInfo: Instance of an HMAC_INFO structure that contains
// information about the HMAC hash.
//
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHmacHash = NULL;
PBYTE pbHash = NULL;
DWORD dwDataLen = 0;
BYTE Data1[] = { 0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64 };
BYTE Data2[] = { 0x6D,0x65,0x73,0x73,0x61,0x67,0x65 };
HMAC_INFO HmacInfo;
struct KEYBLOB
{
BLOBHEADER header;
DWORD size;
};
struct KEYBLOB* keyblob = NULL;
//--------------------------------------------------------------------
// Zero the HMAC_INFO structure and use the SHA1 algorithm for
// hashing.
ZeroMemory(&HmacInfo, sizeof(HmacInfo));
HmacInfo.HashAlgid = CALG_SHA1;
//--------------------------------------------------------------------
// Acquire a handle to the default RSA cryptographic service provider.
if (!CryptAcquireContext(
&hProv, // handle of the CSP
NULL, // key container name
NULL, // CSP name
PROV_RSA_FULL, // provider type
CRYPT_VERIFYCONTEXT)) // no key access is requested
{
printf(" Error in AcquireContext 0x%08x \n",
GetLastError());
goto ErrorExit;
}
keyblob = (KEYBLOB*)malloc(sizeof(KEYBLOB) + sizeof(Data1));
if (keyblob == NULL) {
printf("Error malloc failed to allocate memory of %zu bytes\n",
sizeof(KEYBLOB) + sizeof(Data1));
goto ErrorExit;
}
keyblob->header.bType = PLAINTEXTKEYBLOB;
keyblob->header.bVersion = CUR_BLOB_VERSION;
keyblob->header.reserved = 0;
keyblob->header.aiKeyAlg = CALG_RC2;
keyblob->size = sizeof(Data1);
memcpy((PBYTE)keyblob + sizeof(KEYBLOB), Data1, sizeof(Data1));
if (!CryptImportKey(
hProv,
(PBYTE)keyblob,
sizeof(KEYBLOB) + sizeof(Data1),
0,
CRYPT_IPSEC_HMAC_KEY,
&hKey))
{
printf("Error in CryptImportKey 0x%08x \n",
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, // handle of the CSP.
CALG_HMAC, // HMAC hash algorithm ID
hKey, // key for the hash (see above)
0, // reserved
&hHmacHash)) // address of the hash handle
{
printf("Error in CryptCreateHash 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptSetHashParam(
hHmacHash, // handle of the HMAC hash object
HP_HMAC_INFO, // setting an HMAC_INFO object
(BYTE*)&HmacInfo, // the HMAC_INFO object
0)) // reserved
{
printf("Error in CryptSetHashParam 0x%08x \n",
GetLastError());
goto ErrorExit;
}
if (!CryptHashData(
hHmacHash, // handle of the HMAC hash object
Data2, // message to hash
sizeof(Data2), // number of bytes of data to add
0)) // flags
{
printf("Error in CryptHashData 0x%08x \n",
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, // handle of the HMAC hash object
HP_HASHVAL, // query on the hash value
NULL, // filled on second call
&dwDataLen, // length, in bytes, of the hash
0))
{
printf("Error in CryptGetHashParam 0x%08x \n",
GetLastError());
goto ErrorExit;
}
pbHash = (BYTE*)malloc(dwDataLen);
if (NULL == pbHash)
{
printf("unable to allocate memory\n");
goto ErrorExit;
}
if (!CryptGetHashParam(
hHmacHash, // handle of the HMAC hash object
HP_HASHVAL, // query on the hash value
pbHash, // pointer to the HMAC hash value
&dwDataLen, // length, in bytes, of the hash
0))
{
printf("Error in CryptGetHashParam 0x%08x \n", GetLastError());
goto ErrorExit;
}
// Print the hash to the console.
printf("The hash is: ");
for (DWORD i = 0; i < dwDataLen; i++)
{
printf("%2.2x ", pbHash[i]);
}
printf("\n");
// Free resources.
ErrorExit:
if (hHmacHash)
CryptDestroyHash(hHmacHash);
if (hKey)
CryptDestroyKey(hKey);
if (keyblob)
free(keyblob);
if (hProv)
CryptReleaseContext(hProv, 0);
if (pbHash)
free(pbHash);
return 0;
}