CNG를 사용하여 데이터 서명
데이터 서명은 데이터를 보호하지 않습니다. 데이터의 무결성만 확인합니다. 발신자는 해당 데이터를 해시하고 프라이빗 키를 사용하여 해시를 서명(암호화)합니다. 의도한 수신자는 수신된 데이터의 해시를 만들고, 서명을 해독하여 원래 해시를 가져오고, 두 해시를 비교하여 확인을 수행합니다.
데이터가 서명되면 발신자는 해시 값을 만들고 프라이빗 키를 사용하여 해시를 서명(암호화)합니다. 그런 다음 이 서명은 데이터에 첨부되고 받는 사람에게 메시지를 보냅니다. 서명을 만드는 데 사용된 해시 알고리즘은 받는 사람이 미리 알거나 메시지에서 식별해야 합니다. 이 작업을 수행하는 방법은 메시지 프로토콜에 달려 있습니다.
서명을 확인하기 위해 받는 사람은 메시지에서 데이터와 서명을 추출합니다. 그런 다음, 받는 사람은 데이터에서 다른 해시 값을 만들고, 보낸 사람의 공개 키를 사용하여 서명된 해시의 암호를 해독하고, 두 해시 값을 비교합니다. 값이 동일한 경우 서명이 확인되었으며 데이터는 변경되지 않은 것으로 간주됩니다.
CNG를 사용하여 서명을 만들려면
- CNG 해시 함수를 사용하여 데이터에 대한 해시 값을 만듭니다. 해시를 만드는 방법에 대한 자세한 내용은 CNG를 사용하여 해시 만들기를 참조하세요.
- 해시에 서명하는 비대칭 키를 만듭니다. CNG 키 스토리지 함수를 사용하여 영구 키를 만들거나 CNG 암호화 기본 함수를 사용하여 임시 키를 만들 수 있습니다.
- NCryptSignHash 또는 BCryptSignHash 함수를 사용하여 해시 값에 서명(암호화)합니다. 이 함수는 비대칭 키를 사용하여 해시 값에 서명합니다.
- 데이터와 서명을 원하는 받는 사람에게 보낼 수 있는 메시지에 결합합니다.
CNG를 사용하여 서명을 확인하려면
- 메시지에서 데이터 및 서명을 추출합니다.
- CNG 해시 함수를 사용하여 데이터에 대한 해시 값을 만듭니다. 사용되는 해시 알고리즘은 해시에 서명하는 데 사용된 것과 동일한 알고리즘이어야 합니다.
- 해시에 서명하는 데 사용된 비대칭 키 쌍의 공개 부분을 가져옵니다. 이 키를 가져오는 방법은 키를 만들고 유지하는 방법에 따라 달라집니다. CNG 키 스토리지 함수를 사용하여 키를 만들거나 로드한 경우 NCryptOpenKey 함수를 사용하여 지속형 키를 로드합니다. 키가 임시 키인 경우 키 BLOB에 저장해야 합니다. BCryptImportKeyPair 또는 NCryptImportKey 함수에 이 키 BLOB을 전달해야 합니다.
- 새 해시 값, 서명 및 키 핸들을 NCryptVerifySignature 또는 BCryptVerifySignature 함수에 전달합니다. 이러한 함수는 공개 키를 사용하여 서명을 해독하고 암호 해독된 해시를 2단계에서 계산된 해시와 비교하여 확인을 수행합니다. 서명이 해시와 일치하는 경우 BCryptVerifySignature 함수는 STATUS_SUCCESS 반환하거나 서명이 해시와 일치하지 않으면 STATUS_INVALID_SIGNATURE. 서명이 해시와 일치하는 경우 NCryptVerifySignature 함수는 STATUS_SUCCESS 반환하거나 서명이 해시와 일치하지 않으면 NTE_BAD_SIGNATURE 반환합니다.
데이터 서명 및 확인 예제
다음 예제에서는 암호화 기본 API를 사용하여 지속형 키로 데이터에 서명하고 임시 키로 서명을 확인하는 방법을 보여 줍니다.
// 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 ECDSA 256 signing using CNG
Example for use of BCrypt/NCrypt API
Persisted key for signing and ephemeral key for verification
--*/
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#include <ncrypt.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
static const BYTE rgbMsg[] =
{
0x04, 0x87, 0xec, 0x66, 0xa8, 0xbf, 0x17, 0xa6,
0xe3, 0x62, 0x6f, 0x1a, 0x55, 0xe2, 0xaf, 0x5e,
0xbc, 0x54, 0xa4, 0xdc, 0x68, 0x19, 0x3e, 0x94,
};
void __cdecl wmain(
int argc,
__in_ecount(argc) LPWSTR *wargv)
{
NCRYPT_PROV_HANDLE hProv = NULL;
NCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_KEY_HANDLE hTmpKey = NULL;
SECURITY_STATUS secStatus = ERROR_SUCCESS;
BCRYPT_ALG_HANDLE hHashAlg = NULL,
hSignAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbBlob = 0,
cbSignature = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL,
pbBlob = NULL,
pbSignature = NULL;
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(wargv);
//open an algorithm handle
if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
NULL,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
goto Cleanup;
}
if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hSignAlg,
BCRYPT_ECDSA_P256_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(
hHashAlg,
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(
hHashAlg,
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(
hHashAlg,
&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;
}
//open handle to KSP
if(FAILED(secStatus = NCryptOpenStorageProvider(
&hProv,
MS_KEY_STORAGE_PROVIDER,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptOpenStorageProvider\n", secStatus);
goto Cleanup;
}
//create a persisted key
if(FAILED(secStatus = NCryptCreatePersistedKey(
hProv,
&hKey,
NCRYPT_ECDSA_P256_ALGORITHM,
L"my ecc key",
0,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptCreatePersistedKey\n", secStatus);
goto Cleanup;
}
//create key on disk
if(FAILED(secStatus = NCryptFinalizeKey(hKey, 0)))
{
wprintf(L"**** Error 0x%x returned by NCryptFinalizeKey\n", secStatus);
goto Cleanup;
}
//sign the hash
if(FAILED(secStatus = NCryptSignHash(
hKey,
NULL,
pbHash,
cbHash,
NULL,
0,
&cbSignature,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptSignHash\n", secStatus);
goto Cleanup;
}
//allocate the signature buffer
pbSignature = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbSignature);
if(NULL == pbSignature)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
if(FAILED(secStatus = NCryptSignHash(
hKey,
NULL,
pbHash,
cbHash,
pbSignature,
cbSignature,
&cbSignature,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptSignHash\n", secStatus);
goto Cleanup;
}
if(FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
BCRYPT_ECCPUBLIC_BLOB,
NULL,
NULL,
0,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto Cleanup;
}
pbBlob = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbBlob);
if(NULL == pbBlob)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
if(FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
BCRYPT_ECCPUBLIC_BLOB,
NULL,
pbBlob,
cbBlob,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto Cleanup;
}
if(!NT_SUCCESS(status = BCryptImportKeyPair(
hSignAlg,
NULL,
BCRYPT_ECCPUBLIC_BLOB,
&hTmpKey,
pbBlob,
cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptImportKeyPair\n", status);
goto Cleanup;
}
if(!NT_SUCCESS(status = BCryptVerifySignature(
hTmpKey,
NULL,
pbHash,
cbHash,
pbSignature,
cbSignature,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptVerifySignature\n", status);
goto Cleanup;
}
wprintf(L"Success!\n");
Cleanup:
if(hHashAlg)
{
BCryptCloseAlgorithmProvider(hHashAlg,0);
}
if(hSignAlg)
{
BCryptCloseAlgorithmProvider(hSignAlg,0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if(pbHashObject)
{
HeapFree(GetProcessHeap(), 0, pbHashObject);
}
if(pbHash)
{
HeapFree(GetProcessHeap(), 0, pbHash);
}
if(pbSignature)
{
HeapFree(GetProcessHeap(), 0, pbSignature);
}
if(pbBlob)
{
HeapFree(GetProcessHeap(), 0, pbBlob);
}
if (hTmpKey)
{
BCryptDestroyKey(hTmpKey);
}
if (hKey)
{
NCryptDeleteKey(hKey, 0);
}
if (hProv)
{
NCryptFreeObject(hProv);
}
}