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 함수를 호출하여 개체를 만듭니다. BCRYPT_HASH_REUSABLE_FLAG을(를) dwFlags 매개 변수에 지정합니다.
BCryptHashData 함수를 호출하여 데이터를 해시합니다.
해시 값을 가져오려면 다음 단계를 수행합니다.
- BCryptGetProperty 함수를 호출하여 해시 값의 크기를 가져와서 BCRYPT_HASH_LENGTH 속성을 가져옵니다.
- 값을 저장할 메모리를 할당합니다.
- BCryptFinishHash호출하여 해시 값을 가져옵니다.
새 데이터와 함께 해시 개체를 다시 사용하려면 3단계로 이동합니다.
이 절차를 완료하려면 다음 정리 단계를 수행해야 합니다.
- 해시 핸들을 BCryptDestroyHash 함수에 전달하여 해시 개체를 닫습니다.
- 해시 개체에 할당한 메모리를 해제합니다.
- 더 이상 해시 개체를 만들지 않을 경우 공급자 핸들을 BCryptCloseAlgorithmProvider 함수에 전달하여 알고리즘 공급자를 닫습니다.
- 해시 값 메모리 사용을 마쳤으면 해제합니다.
해시 개체 복제
경우에 따라 일정량의 공통 데이터를 해시한 다음 공통 데이터에서 두 개의 별도 해시 개체를 만드는 것이 유용할 수 있습니다. 이 작업을 수행하려면 두 개의 별도 해시 개체를 만들고 공통 데이터를 두 번 해시할 필요가 없습니다. 단일 해시 개체를 만들고 모든 공통 데이터를 해시 개체에 추가할 수 있습니다. 그런 다음 BCryptDuplicateHash 함수를 사용하여 원래 해시 개체의 복제본을 만들 수 있습니다. 중복 해시 개체는 원본과 동일한 상태 정보와 해시된 데이터를 모두 포함하지만 완전히 독립적인 해시 개체입니다. 이제 각 해시 개체에 고유한 데이터를 추가하고 예제와 같이 해시 값을 가져올 수 있습니다. 이 기술은 많은 양의 일반 데이터를 해시할 때 유용합니다. 원래 해시에 공통 데이터를 한 번만 추가한 다음 해시 개체를 복제하여 고유한 해시 개체를 가져올 수 있습니다.