Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Cet article utilise l’API Cryptography : Next Generation (CNG), qui est l’API recommandée pour les nouvelles applications de chiffrement Windows. Pour la plupart des nouvelles applications, envisagez d’utiliser la courbe elliptique Diffie-Hellman (ECDH) avec une courbe nommée standard telle que P-256 ou P-384, qui offre une sécurité équivalente ou plus forte avec des clés plus courtes et moins de surcharge de gestion des paramètres.
Les fonctions CryptoAPI (CAPI1) héritées (CryptGenKey, CryptExportKey, CryptAcquireContextet ainsi de suite) sont déconseillées. Ne les utilisez pas dans de nouvelles applications.
- Génération de clés Diffie-Hellman
- Échange de clés Diffie-Hellman
- Exportation d’une clé privée Diffie-Hellman
- Exemple de code
- Contenu associé
Génération de clés Diffie-Hellman
Pour générer une paire de clés Diffie-Hellman à l’aide de CNG, procédez comme suit :
Appelez BCryptOpenAlgorithmProvider avec
BCRYPT_DH_ALGORITHMpour obtenir un handle de fournisseur d’algorithmes.Appelez BCryptGenerateKeyPair pour créer la paire de clés, en spécifiant la taille de clé en bits. Utilisez au moins 2048 bits pour une sécurité adéquate ; Les clés 512 bits (telles qu’utilisées dans les exemples CAPI1 hérités) sont faibles par chiffrement et ne doivent pas être utilisées dans le nouveau code.
Définissez les paramètres DH (le premier P et le générateur G) en appelant BCryptSetProperty avec la
BCRYPT_DH_PARAMETERSpropriété avant d’appeler BCryptFinalizeKeyPair. La valeur de propriété doit être une structure BCRYPT_DH_PARAMETER_HEADER suivie immédiatement par la valeur P , puis la valeur G , chaquecbKeyLengthoctet de longueur, dans l’ordre d’octets big-endian.Les deux parties doivent utiliser les mêmes valeurs P et G . Pour le nouveau code, utilisez un groupe standardisé connu plutôt que de générer des paramètres personnalisés ( par exemple, le groupe MODP 2048 bits 14 à partir de RFC 3526 (utilisé dans l’exemple ci-dessous) offre un bon équilibre entre la sécurité et la compatibilité. Le
BCRYPT_DH_PUBLIC_BLOBformat d’exportation inclut P et G, afin qu’un destinataire puisse les extraire de l’objet blob reçu lorsque les deux parties se trouvent sur des ordinateurs ou des processus distincts. Dans un exemple autonome où les deux parties partagent le même processus, le même blob de paramètres peut être réutilisé directement.Appelez BCryptFinalizeKeyPair pour terminer la génération de clé. Cette fonction doit être appelée avant que la clé puisse être utilisée ou exportée.
Lorsque la clé n’est plus nécessaire, appelez BCryptDestroyKey pour libérer le handle de clé et BCryptCloseAlgorithmProvider pour libérer le handle du fournisseur.
Échange de clés Diffie-Hellman
L’objectif de l’algorithme Diffie-Hellman est de permettre à deux ou plusieurs parties de créer et de partager une valeur secrète identique en partageant des informations sur un réseau qui n’est pas sécurisé. L'information qui est partagée sur le réseau, c'est la clé publique Diffie-Hellman de chaque partie. Le processus utilisé par deux parties d’échange de clés est le suivant :
- Les deux parties s’accordent sur Diffie-Hellman paramètres : un nombre premier (P) et un nombre générateur (G).
- La partie 1 envoie sa clé publique Diffie-Hellman à la partie 2.
- La partie 2 calcule le secret partagé à l’aide de sa propre clé privée et de la clé publique de party 1.
- La Partie 2 envoie sa clé publique Diffie-Hellman à la Partie 1.
- La partie 1 calcule le secret partagé à l’aide de sa propre clé privée et de la clé publique de party 2.
- Les deux parties ont désormais le même secret partagé, qui peut être utilisé pour dériver une clé de chiffrement symétrique.
Pour préparer une clé publique Diffie-Hellman pour la transmission :
Après avoir généré et finalisé la paire de clés, appelez BCryptExportKey avec
BCRYPT_DH_PUBLIC_BLOBcomme type d’objet blob pour obtenir les octets de clé publique. L’objet blob inclut les valeurs P, G et clé publique Y, toutes dans l’ordre d’octets big-endian.Transmettez ces octets à l’autre partie sur le réseau.
Note
Le matériau clé dans les objets blob DH CNG (BCRYPT_DH_PUBLIC_BLOB, BCRYPT_DH_PRIVATE_BLOB) est dans l’ordre d’octets big-endian . Il s'agit de l'inverse du format little-endian utilisé par le CryptoAPI, qui est désormais déconseillé (CAPI1). Soyez prudent lors de l'interopération avec le matériau de clé encodé CAPI1.
Pour importer une clé publique Diffie-Hellman et dériver le secret partagé :
Appelez BCryptImportKeyPair avec
BCRYPT_DH_PUBLIC_BLOBpour importer la clé publique de l’autre partie. Cela nécessite un handle de fournisseur d’algorithmes ouvert avecBCRYPT_DH_ALGORITHM.Appelez BCryptSecretAgreement avec votre propre handle de clé privée et le handle de clé publique importé. Cela produit un handle de contrat secret représentant la valeur de secret partagé brut, (Y^X) mod P.
Appelez BCryptDeriveKey pour dériver le matériel de clé utilisable à partir du secret partagé. Utilisez une fonction de dérivation de clé (KDF) appropriée pour votre scénario ;
BCRYPT_KDF_HASHavecSHA-256est un choix à usage général approprié.Utilisez les octets de clé dérivées pour construire une clé symétrique (par exemple, appeler BCryptGenerateSymmetricKey avec
BCRYPT_AES_ALGORITHM) pour le chiffrement ou le déchiffrement ultérieurs.Lorsque vous avez terminé, appelez BCryptDestroySecret pour libérer le handle de contrat secret et BCryptDestroyKey pour libérer tous les handles de clé.
Exportation d’une clé privée Diffie-Hellman
Avertissement
L’exportation de clés privées est une opération sensible à la sécurité. Exportez uniquement le matériel de clé privée si nécessaire et protégez-le de manière appropriée. Pour les clés stockées dans un fournisseur de stockage de clés (KSP), le fournisseur peut restreindre l’exportation en fonction de la stratégie de clé.
Pour exporter une clé privée Diffie-Hellman en tant qu’objet blob de mémoire, appelez BCryptExportKey avec BCRYPT_DH_PRIVATE_BLOB. L’objet blob résultant contient un en-tête BCRYPT_DH_KEY_BLOB suivi des valeurs P, G, Y publiques et X privées, chacune dans l’ordre d’octets big-endian.
Pour importer ultérieurement la clé privée, appelez BCryptImportKeyPair avec BCRYPT_DH_PRIVATE_BLOB.
Exemple de code
L’exemple suivant illustre un échange de clés Diffie-Hellman entre deux parties à l’aide de CNG. Les deux parties dérivent le même matériau clé du secret partagé et comparent les octets dérivés.
Note
Cet exemple utilise des paramètres explicites de Diffie-Hellman et dérive le matériel clé du secret partagé en utilisant BCryptDeriveKey avec BCRYPT_KDF_HASH.
Lors de l’adaptation de cet exemple, assurez-vous que le format des paramètres, les paramètres de dérivation de clé et l’utilisation de clé résultant répondent aux exigences de sécurité et d’interopérabilité de votre application.
#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;
}
Contenu connexe
- API de chiffrement : Nouvelle génération (CNG)
- Vue d’ensemble de la programmation CNG classique
- BCryptOpenAlgorithmProvider
- BCryptGenerateKeyPair
- BCryptFinalizeKeyPair
- BCryptSecretAgreement
- BCryptDeriveKey
- BCRYPT_DH_KEY_BLOB
- BCRYPT_DH_PARAMETER_HEADER
- Identificateurs d’algorithme CNG