C++ で ハッシュ値を生成する。 Windows Cryptography API: Next Generation編

C, C++の標準ライブラリでは、SHA系アルゴリズムを生成するクラス、関数が、提供されていないです。

Windowsでは、OSが提供している CryptoAPIの中にSHA系アルゴリズムを生成する関数が提供されていますが、今回、改めて調べたら非推奨になっていたので、今回は、[Cryptography API: Next Generation](https://learn.microsoft.com/en-us/windows/win32/seccng/creating-a-hash-with-cng?source=recommendations) を使用して、ハッシュ値を生成してみました。

## サンプル

SHA1, SHA256アルゴリズムでハッシュ値を生成し、出力するプログラムです。

```C++
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <bcrypt.h>

#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)

NTSTATUS CreateHash(LPCWSTR pszAlgId, std::vector<UCHAR> &input, std::vector<UCHAR> &output) {

    NTSTATUS                status = STATUS_UNSUCCESSFUL;
    BCRYPT_ALG_HANDLE       hAlg = NULL;
    DWORD                   cbData = 0, cbHash = 0, cbHashObject = 0;

    PBYTE                   pbHashObject = NULL;
    PBYTE                   pbHash = NULL;
    PBYTE                   pbOutput = NULL;
    BCRYPT_HASH_HANDLE      hHash = NULL;

    do {
        //open an algorithm handle
        status = BCryptOpenAlgorithmProvider(&hAlg, pszAlgId, NULL, 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        //calculate the size of the buffer to hold the hash object
        status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        //allocate the hash object on the heap
        pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
        if (NULL == pbHashObject) {
            status = STATUS_UNSUCCESSFUL;
            break;
        }

        //calculate the length of the hash
        status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHash, sizeof(DWORD), &cbData, 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        //allocate the hash buffer on the heap
        pbOutput = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash);
        if (NULL == pbOutput) {
            status = STATUS_UNSUCCESSFUL;
            break;
        }

        //create a hash
        status = BCryptCreateHash(hAlg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        //hash some data
        status = BCryptHashData(hHash, input.data(), input.size(), 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        //close the hash
        status = BCryptFinishHash(hHash, pbOutput, cbHash, 0);
        if (!NT_SUCCESS(status)) {
            break;
        }

        output.resize(cbHash);
        PBYTE p = pbOutput;
        for (size_t i = 0; i < cbHash; i++) {
            output.at(i) = *p;
            p++;
        }

        if (hAlg) {
            BCryptCloseAlgorithmProvider(hAlg, 0);
        }

        if (hHash) {
            BCryptDestroyHash(hHash);
        }

        if (pbHashObject) {
            HeapFree(GetProcessHeap(), 0, pbHashObject);
        }

        if (pbOutput) {
            HeapFree(GetProcessHeap(), 0, pbOutput);
        }

    } while (0);

    return status;

}

int main()
{

    NTSTATUS status = STATUS_UNSUCCESSFUL;
    std::string str("Hello World");
    std::vector<UCHAR> input(str.begin(), str.end());
    
    std::vector<UCHAR> hash1;
    status = CreateHash(BCRYPT_SHA1_ALGORITHM, input, hash1);
    if (!NT_SUCCESS(status)) {
        std::cout << "Error: " << status << std::endl;
        return -1;
    }

    std::cout << "SHA1" << std::endl;
    for (size_t i = 0; i < hash1.size(); i++) {
        std::cout << std::hex << (int)hash1.at(i);
    }
    std::cout << std::endl;

    std::vector<UCHAR> hash2;
    status = CreateHash(BCRYPT_SHA256_ALGORITHM, input, hash2);
    if ( !NT_SUCCESS(status) ) {
        std::cout << "Error: " << status << std::endl;
        return -1;
    }

    std::cout << "SHA256" << std::endl;
    for (size_t i = 0; i < hash2.size(); i++) {
        std::cout << std::hex << (int)hash2.at(i);
    }
    std::cout << std::endl;

    return 0;
}
```