Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Belangrijk
In dit artikel wordt de Cryptography-API: Next Generation (CNG) gebruikt. Dit is de aanbevolen API voor nieuwe Windows cryptografische toepassingen. Voor de meeste nieuwe toepassingen kunt u overwegen Elliptic Curve Diffie-Hellman (ECDH) te gebruiken met een standaard benoemde curve, zoals P-256 of P-384, die gelijkwaardige of sterkere beveiliging biedt met kortere sleutels en minder overhead voor parameterbeheer.
De verouderde CryptoAPI-functies (CAPI1) (CryptGenKey, CryptExportKeyenzovoort CryptAcquireContext) zijn afgeschaft. Gebruik ze niet in nieuwe toepassingen.
- Diffie-Hellman sleutels genereren
- Diffie-Hellman sleutels uitwisselen
- een Diffie-Hellman persoonlijke sleutel exporteren
- voorbeeldcode
- Verwante onderwerpen
Diffie-Hellman sleutels genereren
Voer de volgende stappen uit om een Diffie-Hellman sleutelpaar te genereren met behulp van CNG:
Roep BCryptOpenAlgorithmProvider aan met
BCRYPT_DH_ALGORITHMom een algoritmeprovider-handle te verkrijgen.Roep BCryptGenerateKeyPair aan om het sleutelpaar te maken, waarbij de sleutelgrootte in bits wordt opgegeven. Gebruik ten minste 2048 bits voor voldoende beveiliging; 512-bits sleutels (zoals gebruikt in verouderde CAPI1-voorbeelden) zijn cryptografisch zwak en mogen niet worden gebruikt in nieuwe code.
Stel DH-parameters (de prime P en generator G) in door BCryptSetProperty aan te roepen met de
BCRYPT_DH_PARAMETERSeigenschap voordat u BCryptFinalizeKeyPair aanroept. De eigenschapswaarde moet een BCRYPT_DH_PARAMETER_HEADER structuur zijn, gevolgd door de P-waarde en vervolgens de G-waarde, elkecbKeyLengthbytes in lengte, in big-endian byte-volgorde.Beide partijen moeten dezelfde P - en G-waarden gebruiken. Gebruik voor nieuwe code een bekende gestandaardiseerde groep in plaats van aangepaste parameters te genereren, bijvoorbeeld de 2048-bits MODP-groep 14 van RFC 3526 (gebruikt in het onderstaande voorbeeld) biedt een goede balans tussen beveiliging en compatibiliteit. De
BCRYPT_DH_PUBLIC_BLOBexportindeling bevat P en G, zodat een ontvanger deze kan extraheren uit de ontvangen blob wanneer de twee partijen zich op afzonderlijke machines of processen bevinden. In een zelfstandig voorbeeld waarbij beide partijen hetzelfde proces delen, kan dezelfde parameter-blob rechtstreeks opnieuw worden gebruikt.Roep BCryptFinalizeKeyPair aan om de sleutelgeneratie te voltooien. Deze functie moet worden aangeroepen voordat de sleutel kan worden gebruikt of geëxporteerd.
Wanneer de sleutel niet meer nodig is, roept u BCryptDestroyKey aan om de sleutelgreep vrij te geven en BCryptCloseAlgorithmProvider om de providerhandgreep vrij te geven.
Diffie-Hellman sleutels uitwisselen
Het doel van het Diffie-Hellman algoritme is het mogelijk te maken voor twee of meer partijen om een identieke geheime waarde te maken en te delen door informatie te delen via een netwerk dat niet veilig is. De informatie die via het netwerk wordt gedeeld, is de Diffie-Hellman openbare sleutel van elke partij. Het proces dat door twee sleuteluitwisselingspartijen wordt gebruikt, is als volgt:
- Beide partijen zijn het eens met Diffie-Hellman parameters: een priemgetal (P) en een generatornummer (G).
- Partij 1 stuurt zijn Diffie-Hellman openbare sleutel naar partij 2.
- Party 2 berekent het gedeelde geheim met behulp van een eigen persoonlijke sleutel en de openbare sleutel van party 1.
- Partij 2 stuurt zijn Diffie-Hellman openbare sleutel naar partij 1.
- Party 1 berekent het gedeelde geheim met behulp van een eigen persoonlijke sleutel en de openbare sleutel van Party 2.
- Beide partijen hebben nu hetzelfde gedeelde geheim, dat kan worden gebruikt om een symmetrische versleutelingssleutel af te leiden.
Een Diffie-Hellman openbare sleutel voorbereiden voor verzending:
Nadat u het sleutelpaar hebt gegenereerd en voltooid, roept u BCryptExportKey aan als
BCRYPT_DH_PUBLIC_BLOBblobtype om de bytes van de openbare sleutel op te halen. De blob bevat de waarden P, G en openbare sleutel Y , allemaal in bytevolgorde big-endian.Verzend deze bytes via het netwerk naar de andere partij.
Opmerking
Sleutelmateriaal in CNG DH-blobs (BCRYPT_DH_PUBLIC_BLOB, BCRYPT_DH_PRIVATE_BLOB) bevindt zich in big-endian bytevolgorde . Dit is het tegenovergestelde van de little-endian-indeling die wordt gebruikt door de afgeschafte CryptoAPI (CAPI1). Wees voorzichtig bij het interopereren met CAPI1-gecodeerd sleutelmateriaal.
Een Diffie-Hellman openbare sleutel importeren en het gedeelde geheim afleiden:
Roep BCryptImportKeyPair aan om
BCRYPT_DH_PUBLIC_BLOBde openbare sleutel van de andere partij te importeren. Hiervoor moet een algoritmeprovider worden geopend metBCRYPT_DH_ALGORITHM.Roep BCryptSecretAgreement aan met uw eigen privésleutelhandvat en het geïmporteerde openbare sleutelhandvat. Hiermee wordt een geheime overeenkomst verwerkt die de onbewerkte gedeelde geheime waarde (Y^X) mod P vertegenwoordigt.
Roep BCryptDeriveKey aan om bruikbaar sleutelmateriaal af te leiden van het gedeelde geheim. Gebruik een sleutel derivation-functie (KDF) die geschikt is voor uw scenario;
BCRYPT_KDF_HASHmetSHA-256is een geschikte keuze voor algemeen gebruik.Gebruik de afgeleide sleutelbytes om een symmetrische sleutel te maken (bijvoorbeeld BCryptGenerateSymmetricKey aanroepen met
BCRYPT_AES_ALGORITHM) voor volgende versleuteling of ontsleuteling.Wanneer u klaar bent, roept u BCryptDestroySecret aan om de handle van de geheime overeenkomst vrij te geven en BCryptDestroyKey om alle sleutelhandles vrij te geven.
Een Diffie-Hellman persoonlijke sleutel exporteren
Waarschuwing
Het exporteren van persoonlijke sleutels is een beveiligingsgevoelige bewerking. Exporteer alleen persoonlijk sleutelmateriaal wanneer dit absoluut noodzakelijk is en beveilig het op de juiste wijze. Voor sleutels die zijn opgeslagen in een sleutelopslagprovider (KSP), kan de provider de export beperken op basis van sleutelbeleid.
Als u een Diffie-Hellman persoonlijke sleutel als een geheugen-blob wilt exporteren, roept u BCryptExportKey aan met BCRYPT_DH_PRIVATE_BLOB. De resulterende blob bevat een BCRYPT_DH_KEY_BLOB header gevolgd door de P-, G-, openbare Y- en privé-X-waarden , elk in bytevolgorde big-endian.
Als u de persoonlijke sleutel later wilt importeren, roept u BCryptImportKeyPair aan met BCRYPT_DH_PRIVATE_BLOB.
Voorbeeldcode
In het volgende voorbeeld ziet u een Diffie-Hellman sleuteluitwisseling tussen twee partijen met behulp van CNG. Beide partijen leiden hetzelfde sleutelmateriaal af van het gedeelde geheim en vergelijken de afgeleide bytes.
Opmerking
In dit voorbeeld worden expliciete Diffie-Hellman parameters gebruikt en wordt sleutelmateriaal afgeleid van het gedeelde geheim door BCryptDeriveKey met behulp van BCRYPT_KDF_HASH.
Wanneer u dit voorbeeld aanpast, moet u ervoor zorgen dat de parameterindeling, sleutel-afleidingsinstellingen en het resulterende sleutelgebruik voldoen aan de beveiligings- en interoperabiliteitsvereisten van uw toepassing.
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#include <cstring>
#pragma comment(lib, "bcrypt.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define CHECK(s, fn) if (!NT_SUCCESS(s)) { wprintf(L"Error in %s: 0x%08x\n", fn, s); goto cleanup; }
// 2048-bit MODP Group 14 prime (RFC 3526), big-endian.
// Uses the 2048-bit MODP Group 14 standardized prime.
static const BYTE g_Prime2048[] =
{
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34,
0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,
0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37,
0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,
0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6,
0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,
0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB,
0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,
0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B,
0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F,
0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,
0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10,
0x15,0x72,0x8E,0x5A,0x8A,0xAC,0xAA,0x68,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
// Generator for MODP Group 14 (g = 2), big-endian, zero-padded to 256 bytes.
static BYTE g_Generator2048[256] = { 0 }; // initialized to zero; set g_Generator2048[255] = 2 below
#define KEY_SIZE_BITS 2048
#define KEY_SIZE_BYTES (KEY_SIZE_BITS / 8)
int wmain()
{
int ret = 1;
// Set generator value (g = 2)
g_Generator2048[KEY_SIZE_BYTES - 1] = 2;
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlg1 = NULL, hAlg2 = NULL;
BCRYPT_KEY_HANDLE hKey1 = NULL, hKey2 = NULL;
BCRYPT_KEY_HANDLE hPubKey1 = NULL, hPubKey2 = NULL;
BCRYPT_SECRET_HANDLE hSecret1 = NULL, hSecret2 = NULL;
PBYTE pbPubBlob1 = NULL, pbPubBlob2 = NULL;
PBYTE pbParams = NULL;
PBYTE pbDerivedKey1 = NULL, pbDerivedKey2 = NULL;
ULONG cbPubBlob1 = 0, cbPubBlob2 = 0;
ULONG cbDerivedKey = 0;
// Build the BCRYPT_DH_PARAMETERS blob: header + P + G (all big-endian).
ULONG cbParams = sizeof(BCRYPT_DH_PARAMETER_HEADER) + 2 * KEY_SIZE_BYTES;
pbParams = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbParams);
if (!pbParams) { wprintf(L"Out of memory\n"); goto cleanup; }
BCRYPT_DH_PARAMETER_HEADER* pHeader = (BCRYPT_DH_PARAMETER_HEADER*)pbParams;
pHeader->cbLength = cbParams;
pHeader->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
pHeader->cbKeyLength = KEY_SIZE_BYTES;
memcpy(pbParams + sizeof(BCRYPT_DH_PARAMETER_HEADER), g_Prime2048, KEY_SIZE_BYTES); // P
memcpy(pbParams + sizeof(BCRYPT_DH_PARAMETER_HEADER) + KEY_SIZE_BYTES, g_Generator2048, KEY_SIZE_BYTES); // G
//
// --- Party 1: generate key pair ---
//
status = BCryptOpenAlgorithmProvider(&hAlg1, BCRYPT_DH_ALGORITHM, NULL, 0);
CHECK(status, L"BCryptOpenAlgorithmProvider (Party 1)");
status = BCryptGenerateKeyPair(hAlg1, &hKey1, KEY_SIZE_BITS, 0);
CHECK(status, L"BCryptGenerateKeyPair (Party 1)");
status = BCryptSetProperty(hKey1, BCRYPT_DH_PARAMETERS, pbParams, cbParams, 0);
CHECK(status, L"BCryptSetProperty BCRYPT_DH_PARAMETERS (Party 1)");
status = BCryptFinalizeKeyPair(hKey1, 0);
CHECK(status, L"BCryptFinalizeKeyPair (Party 1)");
// Export Party 1's public key blob (includes P, G, Y).
status = BCryptExportKey(hKey1, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbPubBlob1, 0);
CHECK(status, L"BCryptExportKey size (Party 1)");
pbPubBlob1 = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPubBlob1);
if (!pbPubBlob1) { wprintf(L"Out of memory\n"); goto cleanup; }
status = BCryptExportKey(hKey1, NULL, BCRYPT_DH_PUBLIC_BLOB, pbPubBlob1, cbPubBlob1, &cbPubBlob1, 0);
CHECK(status, L"BCryptExportKey (Party 1)");
//
// --- Party 2: generate key pair using same P and G ---
//
status = BCryptOpenAlgorithmProvider(&hAlg2, BCRYPT_DH_ALGORITHM, NULL, 0);
CHECK(status, L"BCryptOpenAlgorithmProvider (Party 2)");
status = BCryptGenerateKeyPair(hAlg2, &hKey2, KEY_SIZE_BITS, 0);
CHECK(status, L"BCryptGenerateKeyPair (Party 2)");
// Party 2 reuses the same DH parameters as Party 1.
status = BCryptSetProperty(hKey2, BCRYPT_DH_PARAMETERS, pbParams, cbParams, 0);
CHECK(status, L"BCryptSetProperty BCRYPT_DH_PARAMETERS (Party 2)");
status = BCryptFinalizeKeyPair(hKey2, 0);
CHECK(status, L"BCryptFinalizeKeyPair (Party 2)");
// Export Party 2's public key blob.
status = BCryptExportKey(hKey2, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbPubBlob2, 0);
CHECK(status, L"BCryptExportKey size (Party 2)");
pbPubBlob2 = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPubBlob2);
if (!pbPubBlob2) { wprintf(L"Out of memory\n"); goto cleanup; }
status = BCryptExportKey(hKey2, NULL, BCRYPT_DH_PUBLIC_BLOB, pbPubBlob2, cbPubBlob2, &cbPubBlob2, 0);
CHECK(status, L"BCryptExportKey (Party 2)");
//
// --- Party 1: import Party 2's public key, compute shared secret ---
//
status = BCryptImportKeyPair(hAlg1, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPubKey2, pbPubBlob2, cbPubBlob2, 0);
CHECK(status, L"BCryptImportKeyPair Party 2 public key (into Party 1)");
status = BCryptSecretAgreement(hKey1, hPubKey2, &hSecret1, 0);
CHECK(status, L"BCryptSecretAgreement (Party 1)");
// Derive 32 bytes of key material using SHA-256.
BCryptBufferDesc kdfParams = { 0 };
BCryptBuffer kdfBuffer = { 0 };
WCHAR szHashAlg[] = BCRYPT_SHA256_ALGORITHM;
kdfBuffer.BufferType = KDF_HASH_ALGORITHM;
kdfBuffer.cbBuffer = sizeof(szHashAlg);
kdfBuffer.pvBuffer = szHashAlg;
kdfParams.ulVersion = BCRYPTBUFFER_VERSION;
kdfParams.cBuffers = 1;
kdfParams.pBuffers = &kdfBuffer;
status = BCryptDeriveKey(hSecret1, BCRYPT_KDF_HASH, &kdfParams, NULL, 0, &cbDerivedKey, 0);
CHECK(status, L"BCryptDeriveKey size (Party 1)");
pbDerivedKey1 = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDerivedKey);
if (!pbDerivedKey1) { wprintf(L"Out of memory\n"); goto cleanup; }
status = BCryptDeriveKey(hSecret1, BCRYPT_KDF_HASH, &kdfParams, pbDerivedKey1, cbDerivedKey, &cbDerivedKey, 0);
CHECK(status, L"BCryptDeriveKey (Party 1)");
//
// --- Party 2: import Party 1's public key, compute shared secret ---
//
status = BCryptImportKeyPair(hAlg2, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPubKey1, pbPubBlob1, cbPubBlob1, 0);
CHECK(status, L"BCryptImportKeyPair Party 1 public key (into Party 2)");
status = BCryptSecretAgreement(hKey2, hPubKey1, &hSecret2, 0);
CHECK(status, L"BCryptSecretAgreement (Party 2)");
pbDerivedKey2 = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDerivedKey);
if (!pbDerivedKey2) { wprintf(L"Out of memory\n"); goto cleanup; }
ULONG cbDerivedKey2 = cbDerivedKey;
status = BCryptDeriveKey(hSecret2, BCRYPT_KDF_HASH, &kdfParams, pbDerivedKey2, cbDerivedKey2, &cbDerivedKey2, 0);
CHECK(status, L"BCryptDeriveKey (Party 2)");
//
// Verify both parties derived the same key material.
//
if (cbDerivedKey == cbDerivedKey2 && memcmp(pbDerivedKey1, pbDerivedKey2, cbDerivedKey) == 0)
{
wprintf(L"Success: both parties derived the same %u-byte key material.\n", cbDerivedKey);
ret = 0;
}
else
{
wprintf(L"Error: derived keys do not match.\n");
}
cleanup:
if (pbDerivedKey2) { SecureZeroMemory(pbDerivedKey2, cbDerivedKey); HeapFree(GetProcessHeap(), 0, pbDerivedKey2); }
if (pbDerivedKey1) { SecureZeroMemory(pbDerivedKey1, cbDerivedKey); HeapFree(GetProcessHeap(), 0, pbDerivedKey1); }
if (hSecret2) BCryptDestroySecret(hSecret2);
if (hSecret1) BCryptDestroySecret(hSecret1);
if (hPubKey1) BCryptDestroyKey(hPubKey1);
if (hPubKey2) BCryptDestroyKey(hPubKey2);
if (pbPubBlob2) HeapFree(GetProcessHeap(), 0, pbPubBlob2);
if (pbPubBlob1) HeapFree(GetProcessHeap(), 0, pbPubBlob1);
if (hKey2) BCryptDestroyKey(hKey2);
if (hKey1) BCryptDestroyKey(hKey1);
if (hAlg2) BCryptCloseAlgorithmProvider(hAlg2, 0);
if (hAlg1) BCryptCloseAlgorithmProvider(hAlg1, 0);
if (pbParams) HeapFree(GetProcessHeap(), 0, pbParams);
return ret;
}
Verwante inhoud
- Cryptografie-API: Next Generation (CNG)
- Overzicht van typische CNG-programmering
- BCryptOpenAlgorithmProvider
- BCryptGenerateKeyPair
- BCryptFinalizeKeyPair
- BCryptSecretAgreement
- BCryptDeriveKey
- BCRYPT_DH_KEY_BLOB
- BCRYPT_DH_PARAMETER_HEADER
- CNG-algoritmenidentificatoren