CryptoAPI を使ってハッシュを求める

セキュリティ上、パスワードとかの重要な情報は出来る限りファイルやメモリ上に置かない方が良いわけですが、そのための方法として、最初にパスワードを設定する際に、設定されたパスワードのハッシュを求め、パスワードは保存せずにそのハッシュ値だけを保存しておいて、パスワードを比較する際にはその入力されたパスワードのハッシュ値を同じアルゴリズムで求めてやり、そのハッシュ値と保存しておいたハッシュ値とを比較する、という方法があります。
自作のハッシュ関数はいまいち信用できないので、暗号化・復号化のための WindowsAPI(CryptoAPI) を使ってハッシュ値を求めてやりましょう。

Array<BYTE> ToHashCode(const void* p, int size)
{
    HCRYPTPROV hProvider = NULL;
    HCRYPTHASH hHash = NULL;
    DWORD hashSize;
    Array<BYTE> hash;

    if (p == NULL || size < 0)
    {
        goto release;
    }

    // プロバイダの選択
    if (!::CryptAcquireContext(&hProvider, NULL, MS_ENHANCED_PROV,
        PROV_RSA_FULL, 0))
    {
        // ユーザ鍵コンテナを生成して取得
        if (!::CryptAcquireContext(&hProvider, NULL, MS_ENHANCED_PROV,
            PROV_RSA_FULL, CRYPT_NEWKEYSET))
        {
            goto release;
        }
    }

    // ハッシュオブジェクトを生成(今回は SHA-1 を使う)
    if (!::CryptCreateHash(hProvider, CALG_SHA, 0, 0, &hHash))
    {
        goto release;
    }
    // ハッシュ値を計算
    if (!::CryptHashData(hHash, static_cast<const BYTE*>(p), (DWORD)size, 0))
    {
        goto release;
    }

    // ハッシュのサイズを取得(あらかじめアルゴリズムが分かってるなら必要無い)
    // 今回の場合は 160 bit(= 20 byte)になるはず
    if (!::CryptGetHashParam(hHash, HP_HASHVAL, NULL, &hashSize, 0))
    {
        goto release;
    }

    // hashSize バイトを格納出来る大きさを持った配列を生成。
    // アルゴリズムが分かってるなら動的に確保する必要はない。
    hash = Array<BYTE>::New(hashSize);

    // ハッシュ値を取得
    if (!::CryptGetHashParam(hHash, HP_HASHVAL, &hash[0], &hashSize, 0))
    {
        hash = NULL;
        goto release;
    }

release:
    if (hProvider != NULL)
    {
        ::CryptReleaseContext(hProvider, 0);
    }
    if (hHash != NULL)
    {
        ::CryptDestroyHash(hHash)
    }

    return hash;
}

こんな感じかな?


API の詳しい説明は↓へ

CryptoAPI 解説 - 暗号化と電子署名アプリの解説 -