CNG를 사용하여 해시 만들기
해시는 데이터 블록에서 수행되어 데이터의 내용을 나타내는 고유한 해시 값을 만드는 단방향 작업입니다. 해시가 수행되더라도 동일한 데이터에 대해 수행되는 동일한 해시 알고리즘 은 항상 동일한 해시 값을 생성합니다. 데이터가 변경되면 해시 값이 적절하게 변경됩니다.
해시는 해시 값에서 원래 데이터를 재현하는 데 사용되지 않으므로 데이터를 암호화하는 데 유용하지 않습니다. 해시는 비대칭 서명 알고리즘과 함께 사용할 때 데이터의 무결성을 확인하는 데 가장 유용합니다. 예를 들어 문자 메시지를 해시하고 해시에 서명하고 원본 메시지와 함께 서명된 해시 값을 포함하는 경우 받는 사람은 서명된 해시를 확인하고 받은 메시지에 대한 해시 값을 만든 다음 이 해시 값을 원본 메시지에 포함된 서명된 해시 값과 비교할 수 있습니다. 두 해시 값이 동일한 경우 받는 사람은 원래 메시지가 수정되지 않았는지 합리적으로 확신할 수 있습니다.
해시 값의 크기는 특정 해시 알고리즘에 대해 고정됩니다. 즉, 데이터 블록의 크기가 크거나 작더라도 해시 값은 항상 같은 크기가 됩니다. 예를 들어 SHA256 해시 알고리즘의 해시 값 크기는 256비트입니다.
해시 개체 만들기
CNG를 사용하여 해시를 만들려면 다음 단계를 수행합니다.
원하는 알고리즘을 지원하는 알고리즘 공급자를 엽니다. 일반적인 해시 알고리즘에는 MD2, MD4, MD5, SHA-1 및 SHA256이 포함됩니다. BCryptOpenAlgorithmProvider 함수를 호출하고 pszAlgId 매개 변수에 적절한 알고리즘 식별자를 지정합니다. 함수는 공급자에 대한 핸들을 반환합니다.
다음 단계를 수행하여 해시 개체를 만듭니다.
- BCryptGetProperty 함수를 호출하여 BCRYPT_OBJECT_LENGTH 속성을 검색하여 개체의 크기를 가져옵니다.
- 해시 개체를 저장할 메모리를 할당합니다.
- BCryptCreateHash 함수를 호출하여 개체를 만듭니다.
데이터를 해시합니다. 여기에는 BCryptHashData 함수를 한 번 이상 호출하는 작업이 포함됩니다. 각 호출은 지정된 데이터를 해시에 추가합니다.
해시 값을 가져오려면 다음 단계를 수행합니다.
- BCryptGetProperty 함수를 호출하여 값의 크기를 검색하여 BCRYPT_HASH_LENGTH 속성을 가져옵니다.
- 값을 보유할 메모리를 할당합니다.
- BCryptFinishHash 함수를 호출하여 해시 값을 검색합니다. 이 함수가 호출된 후에는 해시 개체가 더 이상 유효하지 않습니다.
이 절차를 완료하려면 다음 정리 단계를 수행해야 합니다.
해시 핸들을 BCryptDestroyHash 함수에 전달하여 해시 개체를 닫습니다.
해시 개체에 할당한 메모리를 해제합니다.
더 이상 해시 개체를 만들지 않을 경우 공급자 핸들을 BCryptCloseAlgorithmProvider 함수에 전달하여 알고리즘 공급자를 닫습니다.
더 많은 해시 개체를 만드는 경우 동일한 유형의 알고리즘 공급자를 여러 번 만들고 삭제하는 대신 알고리즘 공급자를 다시 사용하는 것이 좋습니다.
해시 값 메모리 사용을 마쳤으면 해제합니다.
다음 예제에서는 CNG를 사용하여 해시 값을 만드는 방법을 보여줍니다.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) Microsoft. All rights reserved.
/*++
Abstract:
Sample program for SHA 256 hashing using CNG
--*/
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
static const BYTE rgbMsg[] =
{
0x61, 0x62, 0x63
};
void __cdecl wmain(
int argc,
__in_ecount(argc) LPWSTR *wargv)
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL;
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(wargv);
//open an algorithm handle
if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA256_ALGORITHM,
NULL,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
goto Cleanup;
}
//calculate the size of the buffer to hold the hash object
if(!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
goto Cleanup;
}
//allocate the hash object on the heap
pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject);
if(NULL == pbHashObject)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
//calculate the length of the hash
if(!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&cbHash,
sizeof(DWORD),
&cbData,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
goto Cleanup;
}
//allocate the hash buffer on the heap
pbHash = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHash);
if(NULL == pbHash)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
//create a hash
if(!NT_SUCCESS(status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
NULL,
0,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
goto Cleanup;
}
//hash some data
if(!NT_SUCCESS(status = BCryptHashData(
hHash,
(PBYTE)rgbMsg,
sizeof(rgbMsg),
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
goto Cleanup;
}
//close the hash
if(!NT_SUCCESS(status = BCryptFinishHash(
hHash,
pbHash,
cbHash,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
goto Cleanup;
}
wprintf(L"Success!\n");
Cleanup:
if(hAlg)
{
BCryptCloseAlgorithmProvider(hAlg,0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if(pbHashObject)
{
HeapFree(GetProcessHeap(), 0, pbHashObject);
}
if(pbHash)
{
HeapFree(GetProcessHeap(), 0, pbHash);
}
}
재사용 가능한 해시 개체 만들기
Windows 8 및 Windows Server 2012 시작하여 여러 해시 또는 HMAC를 연속해서 계산해야 하는 시나리오에 재사용 가능한 해시 개체를 만들 수 있습니다. BCryptOpenAlgorithmProvider 함수를 호출할 때 BCRYPT_HASH_REUSABLE_FLAG 지정하여 이 작업을 수행합니다. 모든 Microsoft 해시 알고리즘 공급자는 이 플래그를 지원합니다. 이 플래그를 사용하여 만든 해시 개체는 BCryptCreateHash를 호출하여 새로 만든 것처럼 BCryptFinishHash를 호출한 직후에 다시 사용할 수 있습니다. 재사용 가능한 해시 개체를 만들려면 다음 단계를 수행합니다.
원하는 해시 알고리즘을 지원하는 알고리즘 공급자를 엽니다. BCryptOpenAlgorithmProvider 함수를 호출하고 pszAlgId 매개 변수에 적절한 알고리즘 식별자를 지정하고 dwFlags 매개 변수에 BCRYPT_HASH_REUSABLE_FLAG. 함수는 공급자에 대한 핸들을 반환합니다.
다음 단계를 수행하여 해시 개체를 만듭니다.
- BCryptGetProperty 함수를 호출하여 BCRYPT_OBJECT_LENGTH 속성을 검색하여 개체의 크기를 가져옵니다.
- 해시 개체를 저장할 메모리를 할당합니다.
- BCryptCreateHash 함수를 호출하여 개체를 만듭니다. dwFlags 매개 변수에 BCRYPT_HASH_REUSABLE_FLAG 지정합니다.
BCryptHashData 함수를 호출하여 데이터를 해시합니다.
해시 값을 가져오려면 다음 단계를 수행합니다.
- BCryptGetProperty 함수를 호출하여 해시 값의 크기를 가져와서 BCRYPT_HASH_LENGTH 속성을 가져옵니다.
- 값을 보유할 메모리를 할당합니다.
- BCryptFinishHash를 호출하여 해시 값을 가져옵니다.
해시 개체를 새 데이터와 함께 다시 사용하려면 3단계로 이동합니다.
이 절차를 완료하려면 다음 정리 단계를 수행해야 합니다.
- 해시 핸들을 BCryptDestroyHash 함수에 전달하여 해시 개체를 닫습니다.
- 해시 개체에 할당한 메모리를 해제합니다.
- 더 이상 해시 개체를 만들지 않을 경우 공급자 핸들을 BCryptCloseAlgorithmProvider 함수에 전달하여 알고리즘 공급자를 닫습니다.
- 해시 값 메모리 사용을 마쳤으면 해제합니다.
해시 개체 복제
경우에 따라 일부 양의 공통 데이터를 해시한 다음 공통 데이터에서 두 개의 별도 해시 개체를 만드는 것이 유용할 수 있습니다. 이 작업을 수행하기 위해 두 개의 별도 해시 개체를 만들고 공통 데이터를 두 번 해시할 필요가 없습니다. 단일 해시 개체를 만들고 모든 공통 데이터를 해시 개체에 추가할 수 있습니다. 그런 다음 , BCryptDuplicateHash 함수를 사용하여 원래 해시 개체의 복제본을 만들 수 있습니다. 중복 해시 개체는 원본과 동일한 상태 정보와 해시된 데이터를 모두 포함하지만 완전히 독립적인 해시 개체입니다. 이제 각 해시 개체에 고유 데이터를 추가하고 예제와 같이 해시 값을 가져올 수 있습니다. 이 기술은 많은 양의 공통 데이터를 해시할 때 유용합니다. 원래 해시에 공통 데이터를 한 번만 추가하면 됩니다. 그런 다음 해시 개체를 복제하여 고유한 해시 개체를 가져올 수 있습니다.