Signieren von Daten mit CNG
Die Datensignierung schützt die Daten nicht. Sie überprüft nur die Integrität der Daten. Der Absender hasht diese Daten und signiert (verschlüsselt) den Hash mit Hilfe eines privaten Schlüssels. Der vorgesehene Empfänger führt eine Überprüfung durch, indem er einen Hash der empfangenen Daten erstellt, die Signatur entschlüsselt, um den ursprünglichen Hash zu erhalten, und die beiden Hashes vergleicht.
Wenn Daten signiert sind, erstellt der Absender einen Hashwert und signiert (verschlüsselt) den Hash mithilfe eines privaten Schlüssels. Diese Signatur wird dann an die Daten angefügt und in einer Nachricht an einen Empfänger gesendet. Der Hashalgorithmus, der zum Erstellen der Signatur verwendet wurde, muss vom Empfänger im Voraus bekannt sein oder in der Nachricht identifiziert werden. Dies geschieht bis zum Nachrichtenprotokoll.
Um die Signatur zu überprüfen, extrahiert der Empfänger die Daten und die Signatur aus der Nachricht. Der Empfänger erstellt dann einen weiteren Hashwert aus den Daten, entschlüsselt den signierten Hash mithilfe des öffentlichen Schlüssels des Absenders und vergleicht die beiden Hashwerte. Wenn die Werte identisch sind, wurde die Signatur überprüft, und die Daten werden als unverändert angenommen.
So erstellen Sie eine Signatur mithilfe von CNG
- Erstellen Sie einen Hashwert für die Daten mithilfe der CNG-Hashfunktionen. Weitere Informationen zum Erstellen eines Hashs finden Sie unter Erstellen eines Hashs mithilfe von CNG.
- Erstellen Sie einen asymmetrischen Schlüssel, um den Hash zu signieren. Sie können entweder einen persistenten Schlüssel mit den CNG-Schlüsselspeicherfunktionen oder einen kurzlebigen Schlüssel mit den CNG-Kryptografiegrundtypenfunktionen erstellen.
- Verwenden Sie entweder die NCryptSignHash- oder die BCryptSignHash-Funktion, um den Hashwert zu signieren (verschlüsseln). Diese Funktion signiert den Hashwert mithilfe des asymmetrischen Schlüssels.
- Kombinieren Sie die Daten und die Signatur in einer Nachricht, die an den vorgesehenen Empfänger gesendet werden kann.
So überprüfen Sie eine Signatur mithilfe von CNG
- Extrahieren Sie die Daten und die Signatur aus der Nachricht.
- Erstellen Sie einen Hashwert für die Daten mithilfe der CNG-Hashfunktionen. Der verwendete Hashingalgorithmus muss derselbe Algorithmus sein, der zum Signieren des Hashs verwendet wurde.
- Rufen Sie den öffentlichen Teil des asymmetrischen Schlüsselpaars ab, mit dem der Hash signiert wurde. Wie Sie diesen Schlüssel erhalten, hängt davon ab, wie der Schlüssel erstellt und beibehalten wurde. Wenn der Schlüssel mit den CNG-Schlüsselspeicherfunktionen erstellt oder geladen wurde, verwenden Sie die NCryptOpenKey-Funktion, um den dauerhaften Schlüssel zu laden. Wenn es sich bei dem Schlüssel um einen kurzlebigen Schlüssel handelt, müsste er in einem Schlüssel-BLOB gespeichert worden sein. Sie müssen diesen Schlüssel-BLOB entweder an die BCryptImportKeyPair- oder die NCryptImportKey-Funktion übergeben.
- Übergeben Sie den neuen Hashwert, die Signatur und das Schlüsselhandle entweder an die NCryptVerifySignature- oder die BCryptVerifySignature-Funktion. Diese Funktionen führen die Überprüfung mithilfe des öffentlichen Schlüssels durch, um die Signatur zu entschlüsseln und den entschlüsselten Hash mit dem in Schritt 2 berechneten Hash zu vergleichen. Die Funktion BCryptVerifySignature gibt STATUS_SUCCESS zurück, wenn die Signatur mit dem Hash übereinstimmt oder STATUS_INVALID_SIGNATURE, wenn die Signatur nicht mit dem Hash übereinstimmt. Die Funktion NCryptVerifySignature gibt STATUS_SUCCESS zurück, wenn die Signatur mit dem Hash übereinstimmt oder NTE_BAD_SIGNATURE, wenn die Signatur nicht mit dem Hash übereinstimmt.
Signieren und Überprüfen von Daten (Beispiel)
Das folgende Beispiel zeigt, wie Sie die kryptografischen Grundtyp-APIs verwenden, um Daten mit einem dauerhaften Schlüssel zu signieren und die Signatur mit einem kurzlebigen Schlüssel zu überprüfen.
// 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);
}
}