山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他HMAC-MD5ハッシュを計算(Cryptography API)
概要
HMAC-D5は電子メールのSMTPサーバーのCRAM-MD5認証などに用いられている。
本プログラムは、WindowsのCryptography APIを用いMD5を算出し_tmain関数の変数keyで定義されているキーと変数dataで示されるデーターよりHMAC-MD5を計算し16進数で出力します。
コマンドラインで動作します。
テスト環境
コンパイラ
Visual C++ 2013 Express 32bit/64bitマルチバイト
プラットフォームツールセット Visual Studio 2013 - Windows XP (v120_xp)
実行環境
Windows 7 Enterprise Service Pack 1 64bit(Sandy Bridge-E)HMAC-MD5のアルゴリズム
keyを64byteに拡張
key文字列の後ろに0x00を付加して64byteに拡張します。key文字列が64byte以上の場合は、keyを入力としてMD5ハッシュを求めその結果を64byteに拡張します。
MD5のアルゴリズムについてはMD5ハッシュを計算を参照してください。
opad,ipadの定義
0x5cを64回繰り返したものをopad、0x36を64回繰り返したものをipadと定義します。keyとopad又はipadのxorを算出
拡張されたkeyとipad又はopadのxorを算出します。結果は、key xor ipadをipad key、key xor opadをopad keyと定義します。
ipad keyにデータを付加してMD5ハッシュを算出
ipad keyの後ろにdataを付加し、その結果を入力とするMD5ハッシュを算出します。opad keyに4.のMD5ハッシュを付加して更にMD5ハッシュを算出する
opad keyの後ろに4.のMD5ハッシュの値を付加しそれに対するMD5ハッシュを算出します。このMD5ハッシュがHMAC-MD5です。
プログラムソースの概要
main
変数keyにキーにしたい文字列を指定します。変数dataにメッセージ文字列を指定します。
HASH_UINT128オブジェクトを作成します。
hmac_md5関数によりHMAC-MD5によるハッシュ値を算出します。
hexメンバー関数により16進文字列を取得し_tprintfにより標準出力にハッシュ値を出力します。
hmac_md5関数
引数keyで示されるキー、引数dataで示されるメッセージよりHMAC-MD5ハッシュを計算しHASH_UINT128に結果を格納します。key文字列を64byteへ拡張するためにstrcpy_sで文字列をコピーした後、SecureZeroMemory関数で0x00で初期化しています。
ソースコードを簡単にするためには、あらかじめ64byte分を0x00でクリアしておいて、strcpy_sでコピーする方法がある。文字列の長さ分、0x00及び文字を書き込むので無駄に思えるが、コピー後クリアする場合、文字の長さを取得しておく必要があるので、結局メモリのアクセス回数は変わらない。デバックプロジェクトでstrcpy_sを使用すると、文字列の後の0x00の後ろにデバック用の文字0xfeが連続して書き込まれる。(Visual C++ 2013で確認)これによりデバック時はあらかじめ64byteを0x00でクリアしてもstrcpy_sにより0xfeが書き込まれるので、バクが発生してします。
ZeroMemoryを使用しないわけは最適化により削除される可能性があるためである。
MD5ハッシュの算出には、MD5ハッシュを計算(Cryptography API)と同じコードを用いています。
HASH_UINT128
ハッシュ値の保存及びハッシュ値から16進数の文字列を取得するための共用体です。ソースコード
hmac_md5.cpp
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <wincrypt.h>
union HASH_UINT128{
UINT64 u64[2];
UINT32 u32[4];
BYTE u8[16];
void hex(char* s){
sprintf_s(s, 33, "%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x", u8[0], u8[1], u8[2], u8[3], u8[4], u8[5], u8[6], u8[7], u8[8], u8[9], u8[10], u8[11], u8[12], u8[13], u8[14], u8[15]);
}
};
bool md5Hashed(char* key, DWORD dwLength, HASH_UINT128* u128){
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){
MessageBox(0, _TEXT("CryptAcquireContext"), _TEXT("Error"), MB_OK);
return false;
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)){
MessageBox(0, _TEXT("CryptCreateHash"), _TEXT("Error"), MB_OK);
CryptReleaseContext(hProv, 0);
return false;
}
if (!CryptHashData(hHash, (BYTE*)key, dwLength, 0)){
MessageBox(0, _TEXT("CryptCreateHash"), _TEXT("Error"), MB_OK);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return false;
}
DWORD dwRead = 16;
if (!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)u128, &dwRead, 0)){
MessageBox(0, _TEXT("CryptGetHashParam"), _TEXT("Error"), MB_OK);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return false;
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return true;
}
void hmac_md5(char* key,char* data,HASH_UINT128* dtc){
int n;
char temp[64+16];
int len = (int)strlen(key);
strcpy_s(temp, sizeof(temp), key);
SecureZeroMemory(temp+len, 64-len);
for (n = 0; n < 64; n++)
temp[n] = temp[n] ^ 0x36;
strcpy_s(temp+64, sizeof(temp)-64,data);
md5Hashed(temp, 64+(int)strlen(data), dtc);
strcpy_s(temp, sizeof(temp), key);
SecureZeroMemory(temp + len, 64 - len);
for (n = 0; n < 64; n++)
temp[n] = temp[n] ^ 0x5c;
memcpy(temp + 64, dtc, 16);
md5Hashed(temp, 64+16, dtc);
}
void main(void){
char* key = "pass";
char* data = "hello";
HASH_UINT128 hash;
char hexs[34];
hmac_md5(key, data, &hash);
hash.hex(hexs);
printf("HMAC MD5 %s\n", hexs);
}
Copyright (C) 2012 山本ワールド All Rights Reserved.