Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Importante
Questo articolo usa l'API Cryptography: Next Generation (CNG), che è l'API consigliata per le nuove applicazioni di crittografia Windows. Per la maggior parte delle nuove applicazioni, è consigliabile usare la curva ellittica Diffie-Hellman (ECDH) con una curva denominata standard, ad esempio P-256 o P-384, che garantisce una sicurezza equivalente o più avanzata con chiavi più brevi e un sovraccarico di gestione dei parametri inferiore.
Le funzioni CryptoAPI legacy (CAPI1) (CryptGenKey, CryptExportKey, CryptAcquireContexte così via) sono deprecate. Non usarle nelle nuove applicazioni.
- generazione di chiavi Diffie-Hellman
- scambio di chiavi Diffie-Hellman
- Esportazione di una chiave privata Diffie-Hellman
- esempio di codice
- Contenuto correlato
Generazione di chiavi Diffie-Hellman
Per generare una coppia di chiavi Diffie-Hellman tramite CNG, seguire questa procedura:
Chiamare BCryptOpenAlgorithmProvider con
BCRYPT_DH_ALGORITHMper ottenere un handle del provider di algoritmi.Chiamare BCryptGenerateKeyPair per creare la coppia di chiavi, specificando le dimensioni della chiave in bit. Usare almeno 2048 bit per una sicurezza adeguata; Le chiavi a 512 bit (usate negli esempi CAPI1 legacy) sono crittograficamente deboli e non devono essere usate nel nuovo codice.
Impostare i parametri DH (il primo P e il generatore G) chiamando BCryptSetProperty con la
BCRYPT_DH_PARAMETERSproprietà prima di chiamare BCryptFinalizeKeyPair. Il valore della proprietà deve essere una struttura BCRYPT_DH_PARAMETER_HEADER seguita immediatamente dal valore P e quindi dal valore G , ognicbKeyLengthbyte in lunghezza, in ordine di byte big-endian.Entrambe le parti devono utilizzare gli stessi valori P e G . Per il nuovo codice, usare un gruppo standardizzato noto anziché generare parametri personalizzati, ad esempio il gruppo MODP a 2048 bit 14 da RFC 3526 (usato nell'esempio seguente) offre un buon equilibrio tra sicurezza e compatibilità. Il
BCRYPT_DH_PUBLIC_BLOBformato di esportazione include P e G, in modo che un destinatario possa estrarli dal BLOB ricevuto quando le due parti si trovano in computer o processi separati. In un esempio autonomo in cui entrambe le parti condividono lo stesso processo, lo stesso blob di parametri può essere riutilizzato direttamente.Chiama BCryptFinalizeKeyPair per completare la generazione della chiave. Questa funzione deve essere chiamata prima che la chiave possa essere usata o esportata.
Quando la chiave non è più necessaria, chiamare BCryptDestroyKey per rilasciare l'handle della chiave e BCryptCloseAlgorithmProvider per rilasciare l'handle del provider.
Scambio di chiavi di Diffie-Hellman
Lo scopo dell'algoritmo Diffie-Hellman è quello di consentire a due o più parti di creare e condividere un valore segreto identico condividendo le informazioni in una rete non sicura. L'informazione che viene condivisa in rete è la chiave pubblica Diffie-Hellman di ciascuna parte. Il processo usato da due parti che effettuano lo scambio di chiavi è il seguente:
- Entrambe le parti sono d'accordo sui parametri Diffie-Hellman: un numero primo (P) e un numero generatore (G).
- La parte 1 invia la sua chiave pubblica Diffie-Hellman alla Parte 2.
- La parte 2 calcola il segreto condiviso usando la propria chiave privata e la chiave pubblica della parte 1.
- Parte 2 invia la sua chiave pubblica Diffie-Hellman alla Parte 1.
- La parte 1 calcola il segreto condiviso usando la propria chiave privata e la chiave pubblica della parte 2.
- Entrambe le parti ora hanno lo stesso segreto condiviso, che può essere usato per derivare una chiave di crittografia simmetrica.
Per preparare una chiave pubblica Diffie-Hellman per la trasmissione:
Dopo aver generato e finalizzato la coppia di chiavi, chiamare BCryptExportKey utilizzando
BCRYPT_DH_PUBLIC_BLOBcome tipo di blob per ottenere i byte della chiave pubblica. Il blob include i valori P, G e Y della chiave pubblica, tutti nell'ordine big-endian.Trasmettere quei byte all'altra parte attraverso la rete.
Annotazioni
Il materiale della chiave nei BLOB DH CNG (BCRYPT_DH_PUBLIC_BLOB, BCRYPT_DH_PRIVATE_BLOB) è in formato byte big-endian. Questo è l'opposto del formato little-endian usato dalla CryptoAPI deprecata (CAPI1). Prestare attenzione quando si interagisce con il materiale della chiave con codifica CAPI1.
Per importare una chiave pubblica Diffie-Hellman e derivare il segreto condiviso:
Chiamare BCryptImportKeyPair con
BCRYPT_DH_PUBLIC_BLOBper importare la chiave pubblica dell'altra parte. Questo richiede un handle del provider di algoritmi aperto conBCRYPT_DH_ALGORITHM.Chiamare BCryptSecretAgreement utilizzando il proprio handle della chiave privata e l'handle della chiave pubblica importata. Questo produce un handle di contratto segreto che rappresenta il valore non elaborato del segreto condiviso, (Y^X) mod P.
Chiamare BCryptDeriveKey per derivare il materiale della chiave utilizzabile dal segreto condiviso. Usare una funzione di derivazione chiave (KDF) appropriata per lo scenario;
BCRYPT_KDF_HASHconSHA-256è una scelta adatta per utilizzo generico.Usare i byte di chiave derivati per costruire una chiave simmetrica , ad esempio chiamare BCryptGenerateSymmetricKey con
BCRYPT_AES_ALGORITHMper la crittografia o la decrittografia successiva.Al termine, chiama BCryptDestroySecret per rilasciare l'handle del contratto segreto e BCryptDestroyKey per rilasciare tutti gli handle di chiave.
Esportazione di una chiave privata Diffie-Hellman
Attenzione
L'esportazione di chiavi private è un'operazione sensibile alla sicurezza. Esportare solo materiale di chiave privata quando assolutamente necessario e proteggerlo in modo appropriato. Per le chiavi archiviate in un provider di archiviazione chiavi (KSP), il provider può limitare l'esportazione in base ai criteri delle chiavi.
Per esportare una chiave privata Diffie-Hellman come BLOB di memoria, chiamare BCryptExportKey con BCRYPT_DH_PRIVATE_BLOB. Il BLOB risultante contiene un'intestazione BCRYPT_DH_KEY_BLOB seguita dai valori P, G, public Y e private X , ognuno in ordine di byte big-endian.
Per importare successivamente la chiave privata, chiamare BCryptImportKeyPair con BCRYPT_DH_PRIVATE_BLOB.
Codice di esempio
Nell'esempio seguente viene illustrato uno scambio di chiavi Diffie-Hellman tra due parti che usano CNG. Entrambe le parti derivano lo stesso materiale della chiave dal segreto condiviso e confrontano i byte derivati.
Annotazioni
Questo esempio usa parametri espliciti Diffie-Hellman e deriva il materiale della chiave dal segreto condiviso usando BCryptDeriveKey con BCRYPT_KDF_HASH.
Quando si adatta questo esempio, assicurarsi che il formato del parametro, le impostazioni di derivazione chiave e l'utilizzo delle chiavi risultante soddisfino i requisiti di sicurezza e interoperabilità dell'applicazione.
#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;
}
Contenuti correlati
- API di crittografia: Next Generation (CNG)
- Panoramica della programmazione CNG tipica
- BCryptOpenAlgorithmProvider
- BCryptGenerateKeyPair
- BCryptFinalizeKeyPair
- BCryptSecretAgreement
- BCryptDeriveKey
- BCRYPT_DH_KEY_BLOB
- BCRYPT_DH_PARAMETER_HEADER
- Identificatori di algoritmo CNG