Platform: android native, windows mobile, Symbian
1. HMAC introduction
You may regard it as a Hash algorithm with a secret key. The output of HMAC is based on the Hash algorithm.
HMAC http://tools.ietf.org/html/rfc2104
HMAC 256 http://tools.ietf.org/html/rfc4231
2. Implementation based on OPENSSL
The OPENSSL provide an API that encapsulates the HMAC calculation, you only need to choose a Hash algorithm. Sample code:
const EVP_MD *mdsha = EVP_sha();
HMAC(mdsha, pKey->bytes, pKey->length,
pPlain->bytes, pPlain->length,
pHmac->bytes, &iOutLen);
3. Implementation based on Crypto API(CSP)
a. The first question is how to import a known key into the key handle.
The Microsoft CSP provides many ways to generated or derive a session key, but never provides as API that with a parameter to pass a secrete key. It seems that the Microsoft does not like the session key (symmetric key) being exposed outside the key container. In terms of security, it is a good solution.
We need a CRYPT_DATA_BLOB to import secrete key into a HMAC key handle.
Here we need to pay attention to the length of the HMAC key. According to the HMAC specification the length of HMAC key is not fixed. So we need to choose a encryption algorithm that has the same key length as your HMAC key length. I recommend for 5-16 use RC4.
Sample code
// we prepare the HMAC key handle here
unsigned char pKeyData[128] = {0};
unsigned int keyDataSize = 32;
unsigned char messageData[128] = {0};
unsigned int messageDataSize = 40;
strcpy((char *)pKeyData, "12345678901234567890123456789012" );
strncpy((char *)messageData, "1234567890123456789012345678901234567890" , messageDataSize);
typedef struct _keyBlob{
BLOBHEADER hdr;
DWORD cbKeySize;
BYTE rgbKeyData [];
} keyBlob;
BYTE bKeyBuf[1024] = {0};
keyBlob *pBlob = NULL;
int iBlobLen = 0;
pBlob = (keyBlob *)bKeyBuf;
pBlob->hdr.bType = PLAINTEXTKEYBLOB;
pBlob->hdr.aiKeyAlg = CALG_AES_256; // we need a 32 key length alg
pBlob->hdr.bVersion = 0x02;
pBlob->cbKeySize = keyDataSize;
memcpy(pBlob->rgbKeyData, pKeyData, keyDataSize);
iBlobLen = sizeof (BLOBHEADER) + sizeof (DWORD) + keyDataSize;
if (!CryptImportKey(
hProv, // handle of the CSP
bKeyBuf, // key container name
iBlobLen, //sizeof(HmacKeyBlob),
NULL,
0,
&hKey))
{
printf(" Error in AcquireContext 0x%08x /n" ,
GetLastError());
goto ErrorExit;
}
b. Get hash value
Sample code
HMAC_INFO HmacInfo;
// Zero the HMAC_INFO structure and use the SHA1 algorithm for hashing.
ZeroMemory(&HmacInfo, sizeof (HmacInfo));
// choose a Hash alg
HmacInfo.HashAlgid = CALG_SHA_256;
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
messageData, // message to hash
messageDataSize, // 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;
}
For more info:
http://msdn.microsoft.com/en-us/library/aa382383%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa387453%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa380207%28v=vs.85%29.aspx
4. Implementation based on third party algorithm library
According to the specification rfc2104, we may implement the HMAC calculation, what we need is a third party Hash algorithm.
Sample code
unsigned char k_ipad[65] = {0}; /* inner padding - key XORd with ipad */
unsigned char k_opad[97] = {0}; /* outer padding - key XORd with opad */
const int iPadLen = 64;
int i = 0;
unsigned char out1[1024] = {0};
unsigned char out2[1024] = {0};
unsigned int iOutLen = 0;
sha256_ctx cx[1]; // for multiply clock of first round hash
memcpy( k_ipad, pKey->bytes, pKey->length);
memcpy( k_opad, pKey->bytes, pKey->length);
for (i = 0; i < iPadLen; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
// first hash
sha256_begin(cx);
sha256_hash(k_ipad, iPadLen, cx);
sha256_hash(pPlain->bytes, pPlain->length, cx);
sha256_end(out1, cx);
// second hash
memcpy(k_opad + iPadLen, out1, MAC_LEN);
sha256(pHmac->bytes, k_opad, iPadLen + MAC_LEN);
pHmac->length = MAC_LEN;
5. Some concern on the majority mobile platform.
a. For the android you may use the crypto package. But for the sake of security I recommend to implement HMAC with third party hash library at the android native side.
b. For the windows mobile, you may use the CSP directly, and use the derived HMAC key instead of the imported HMAC key.
c. The Symbian provides a simplified Openssl API, you may use it directly.