last update: 2013/09/03

2004/05/26

MD5をだしてみる

もう3週間ぐらい前の話ですが。

ファイルの改ざんをチェックする方法としてMD5を算出して、オリジナルのファイルとの相違がないことを調べるという方法があります。余談ですが、Winnyで特定のファイルを示すためのハッシュもファイルのMD5らしいですね。

というわけで、ちょっとファイルの改ざんチェックをしようと思ってMD5を算出してみようと思ったのですが、MD5ってどうやって算出するんだろうなぁ、というか、Windowsにファイルハンドル渡したらMD5がぽんと出てくるようなAPIは用意されていないのか!

…用意されてなかったようですが、某所で聞いたらWinNT系で使えるCryptAPIでMD5を算出することができるらしいので、これでやってみることに。ということで、「ファイルハンドルを渡したらぽんっとMD5が出てくるようなAPI」をここに書いておきます。

#include <windows.h>
#include <wincrypt.h>

char* CalcMD5(HANDLE hFile, char* szBuf){
    *szBuf=NULL;
    if(hFile==INVALID_HANDLE_VALUE){ // fail to open
        return NULL;
    }

    // calc MD5
    HCRYPTPROV hCryptProv=NULL;
    HCRYPTHASH hHash;
    // PROV_RSA_FULL support MD5 hash
    if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)){ 
        return NULL;
    }
    // create empty md5 hash
    if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash)){ 
        return NULL;
    }

    // read file and add to hashdata
    BYTE tmpBuf[4096];
    DWORD dwRead;
    while(ReadFile(hFile, tmpBuf, 4096, &dwRead, NULL) && dwRead){
        if(!CryptHashData(hHash, tmpBuf, dwRead, 0)){ // add hash 
            return NULL;
        }
    }

    // calculate hash
    DWORD dwHashLen;
    BYTE *pbHash;
    if(!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, NULL)){ // get hash size
        return NULL;
    }
    if(!(pbHash=new BYTE[dwHashLen])){
        return NULL;
    }
    if(!CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, NULL)){ // get hash 
        delete [] pbHash;
        return NULL;
    }

    // print hash
    char tmp[16];
    *szBuf=NULL;
    for(DWORD i=0;i<dwHashLen; i++){
        sprintf(tmp, _t("%2.2x"), pbHash[i]);
        strcat(szBuf, tmp);
    }

    // destroy
    delete [] pbHash;

    return szBuf;
}

まぁ、たぶんこんな感じで動くと思います。MD5を格納するのに十分な配列(基本的には32バイトぐらいは必要?)と、Read可能なファイルハンドルをわたすと、配列にMD5を突っ込んでくれます。算出に失敗したときはNULL、成功したときは配列の先頭を返します。

…こんな関数を書かなくても、本気になればRFC1321よめばちゃんとCのサンプルコードも載ってるのでそっち使った方が早かったり…とか言っちゃダメですよ。

comments powered by Disqus