ファイルやIStreamのハッシュを計算する

Windows 95 OSR2以降に対応するCryptHashDataを利用すると簡単にハッシュを求めれる。ここではこのAPIを利用して、メモリ上のデータ、ファイル、IStreamのデータのハッシュを求めるクラスを作成した。簡単に利用できるのはMD2、MD4、MD5、SHA-1ハッシュだが、SHA-256などほかのハッシュもdwFlagsに指定することで計算できる。

「DnpHashEx.h」として保存
#pragma once

#include <wincrypt.h>
#include "objidl.h"


//
//Crypt関連APIを利用してハッシュを計算する
//
//Windows95OSR2以降
//
class	CDnpHashEx
{
public:


	//
	//	MD2ハッシュ計算(128ビット)
	//
	bool	GetMD2Hash(const void* pData,DWORD dwLen,BYTE pcbHashData[16])
	{
		return	GetHash(pData,dwLen,pcbHashData,16,CALG_MD2);
	}


	//
	//	MD4ハッシュ計算(128ビット)
	//
	bool	GetMD4Hash(const void* pData,DWORD dwLen,BYTE pcbHashData[16])
	{
		return	GetHash(pData,dwLen,pcbHashData,16,CALG_MD4);
	}


	//
	//	MD5ハッシュ計算(128ビット)
	//
	bool	GetMD5Hash(const void* pData,DWORD dwLen,BYTE pcbHashData[16])
	{
		return	GetHash(pData,dwLen,pcbHashData,16,CALG_MD5);
	}


	//
	//	SHA-1ハッシュ計算(160ビット)
	//
	bool	GetSHA1Hash(const void* pData,DWORD dwLen,BYTE pcbHashData[20])
	{
		return	GetHash(pData,dwLen,pcbHashData,20,CALG_SHA1);
	}




	//
	//	各種ハッシュ計算
	//
	bool	GetHash(const void* pData,DWORD dwLen,BYTE* pcbHashData,DWORD dwHashLen,DWORD dwFlags)
	{
		bool		ret;
		HCRYPTPROV	hCryptProv;
		HCRYPTHASH	hHash;
		BYTE		pbHash[0x7f];

		::ZeroMemory(pcbHashData,dwHashLen);
		if(pData == NULL || dwLen == 0 || dwHashLen == 0 || dwHashLen > 0x7f)
		{
			ATLASSERT(0);
			return	false;
		}

		hHash = NULL;
		hCryptProv = NULL;
		ret = false;
		if(::CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
		{
			if(::CryptCreateHash(hCryptProv,dwFlags,0,0,&hHash))
			{
				if(::CryptHashData(hHash,(BYTE*)pData,dwLen,0))
				{
					if(::CryptGetHashParam(hHash,HP_HASHVAL,pbHash,&dwHashLen,0)) 
					{
						::memcpy_s(pcbHashData,dwHashLen,pbHash,dwHashLen);
						ret = true;
					}
				}
			}
		}

		if(hHash)
			::CryptDestroyHash(hHash);
		if(hCryptProv)
			::CryptReleaseContext(hCryptProv,0);

		if(ret == false)
			::ZeroMemory(pcbHashData,dwHashLen);

		return	ret;
	}



	//
	//	MD2ハッシュ計算(128ビット)
	//
	bool	GetMD2FileHash(LPCTSTR pszFile,BYTE pcbHashData[16])
	{
		return	GetFileHash(pszFile,pcbHashData,16,CALG_MD2);
	}


	//
	//	MD4ハッシュ計算(128ビット)
	//
	bool	GetMD4FileHash(LPCTSTR pszFile,BYTE pcbHashData[16])
	{
		return	GetFileHash(pszFile,pcbHashData,16,CALG_MD4);
	}


	//
	//	MD5ハッシュ計算(128ビット)
	//
	bool	GetMD5FileHash(LPCTSTR pszFile,BYTE pcbHashData[16])
	{
		return	GetFileHash(pszFile,pcbHashData,16,CALG_MD5);
	}


	//
	//	SHA-1ハッシュ計算(160ビット)
	//
	bool	GetSHA1FileHash(LPCTSTR pszFile,BYTE pcbHashData[20])
	{
		return	GetFileHash(pszFile,pcbHashData,20,CALG_SHA1);
	}




	//
	//	ファイルハッシュ計算
	//
	bool	GetFileHash(LPCTSTR pszFile,BYTE* pcbHashData,DWORD dwHashLen,DWORD dwFlags)
	{
		bool		ret;
		HCRYPTPROV	hCryptProv;
		HCRYPTHASH	hHash;
		BYTE		pbHash[0x7f];
		HANDLE		hFile;
		ULARGE_INTEGER	lnSize;

		::ZeroMemory(pcbHashData,dwHashLen);
		if(pszFile == 0 || dwHashLen == 0 || dwHashLen > 0x7f)
			return	false;


		hFile = ::CreateFile(pszFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		if(hFile == INVALID_HANDLE_VALUE)
			return	false;


		//ファイルサイズ取得
		lnSize.LowPart = ::GetFileSize(hFile,&lnSize.HighPart);

		hHash = NULL;
		hCryptProv = NULL;
		ret = false;
		if(::CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
		{
			if(::CryptCreateHash(hCryptProv,dwFlags,0,0,&hHash))
			{
				BOOL			bRet;
				ULARGE_INTEGER	lnPos;
				DWORD	dwRead;
				BYTE	pcbTmp[64000];

				bRet = FALSE;
				lnPos.QuadPart = 0;
				while(1)
				{
					//64KBずつ処理
					bRet = ::ReadFile(hFile,pcbTmp,64000,&dwRead,NULL);
					if(bRet == FALSE)
						break;

					lnPos.QuadPart += dwRead;

					//ハッシュ計算
					bRet = ::CryptHashData(hHash,pcbTmp,dwRead,0);
					if(bRet == FALSE)
						break;

					//処理バイト数とファイルサイズが一致したらbreak
					if(lnPos.QuadPart == lnSize.QuadPart)
						break;
				}

				//ハッシュ取得
				if(bRet && ::CryptGetHashParam(hHash,HP_HASHVAL,pbHash,&dwHashLen,0)) 
				{
					::memcpy_s(pcbHashData,dwHashLen,pbHash,dwHashLen);
					ret = true;
				}
			}
		}

		if(hHash)
			::CryptDestroyHash(hHash);
		if(hCryptProv)
			::CryptReleaseContext(hCryptProv,0);

		::CloseHandle(hFile);

		if(ret == false)
			::ZeroMemory(pcbHashData,dwHashLen);

		return	ret;
	}






	//
	//	MD2ハッシュ計算(128ビット)
	//
	bool	GetMD2IStreamHash(IStream* pIStream,BYTE pcbHashData[16])
	{
		return	GetIStreamHash(pIStream,pcbHashData,16,CALG_MD2);
	}


	//
	//	MD4ハッシュ計算(128ビット)
	//
	bool	GetMD4IStreamHash(IStream* pIStream,BYTE pcbHashData[16])
	{
		return	GetIStreamHash(pIStream,pcbHashData,16,CALG_MD4);
	}


	//
	//	MD5ハッシュ計算(128ビット)
	//
	bool	GetMD5IStreamHash(IStream* pIStream,BYTE pcbHashData[16])
	{
		return	GetIStreamHash(pIStream,pcbHashData,16,CALG_MD5);
	}


	//
	//	SHA-1ハッシュ計算(160ビット)
	//
	bool	GetSHA1IStreamHash(IStream* pIStream,BYTE pcbHashData[20])
	{
		return	GetIStreamHash(pIStream,pcbHashData,20,CALG_SHA1);
	}


	//
	//	IStreamからのハッシュ計算
	//
	bool	GetIStreamHash(IStream* pIStream,BYTE* pcbHashData,DWORD dwHashLen,DWORD dwFlags)
	{
		bool		ret;
		HCRYPTPROV	hCryptProv;
		HCRYPTHASH	hHash;
		BYTE		pbHash[0x7f];
		HRESULT			hr;
		LARGE_INTEGER	lnPos;
		STATSTG			sStatstg;

		::ZeroMemory(pcbHashData,dwHashLen);
		if(pIStream == 0 || dwHashLen == 0 || dwHashLen > 0x7f)
			return	false;

		//ファイル先頭へシーク
		lnPos.QuadPart = 0;
		hr = pIStream->Seek(lnPos,STREAM_SEEK_SET,NULL);
		if(FAILED(hr))
			return	false;

		//ファイルサイズ取得
		::ZeroMemory(&sStatstg,sizeof(STATSTG));
		hr = pIStream->Stat(&sStatstg,STATFLAG_DEFAULT);
		if(FAILED(hr))
			return	false;

		hHash = NULL;
		hCryptProv = NULL;
		ret = false;
		if(::CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
		{
			if(::CryptCreateHash(hCryptProv,dwFlags,0,0,&hHash))
			{
				BOOL			bRet;
				ULARGE_INTEGER	lnPos;
				ULONG	nRead;
				BYTE	pcbTmp[64000];

				bRet = FALSE;
				lnPos.QuadPart = 0;
				while(1)
				{
					//64KBずつ処理
					hr = pIStream->Read(pcbTmp,64000,&nRead);
					if(FAILED(hr))
						break;

					lnPos.QuadPart += nRead;

					//ハッシュ計算
					bRet = ::CryptHashData(hHash,pcbTmp,(DWORD)nRead,0);
					if(bRet == FALSE)
						break;

					//処理バイト数とファイルサイズが一致したらbreak
					if(lnPos.QuadPart == sStatstg.cbSize.QuadPart)
						break;
				}

				//ハッシュ取得
				if(bRet && ::CryptGetHashParam(hHash,HP_HASHVAL,pbHash,&dwHashLen,0)) 
				{
					::memcpy_s(pcbHashData,dwHashLen,pbHash,dwHashLen);
					ret = true;
				}
			}
		}

		if(hHash)
			::CryptDestroyHash(hHash);
		if(hCryptProv)
			::CryptReleaseContext(hCryptProv,0);

		if(ret == false)
			::ZeroMemory(pcbHashData,dwHashLen);

		return	ret;
	}
};

依存環境:ATL
#include "atlstr.h"

#include "DnpHashEx.h"

//
//	テスト用関数
//
void	Test(void)
{
	bool		ret;
	int			i;
	CAtlString	strTmp;
	CAtlString	strMessage;
	BYTE		pcbData[20];
	CDnpHashEx	cHash;


	//ファイルのSHA-1ハッシュを計算
	ret = cHash.GetSHA1FileHash(_T("c:\\boot.ini"),pcbData);

	if(ret == false)
	{
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
		return;
	}

	for(i = 0; i < 20; i++)
	{
		strTmp.Format(_T("%x"),pcbData[i]);
		strMessage += strTmp;
	}

	::MessageBox(NULL,strMessage,_T(""),MB_OK);
}

プロジェクトファイルをダウンロード


カテゴリー「システム情報」 のエントリー