Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Megjegyzés:
Egyes információk egy előzetes termékre vonatkoznak, amely jelentősen módosítható a kereskedelmi forgalomba kerülés előtt. A Microsoft nem vállal kifejezett vagy hallgatólagos szavatosságot az itt megadott információkra vonatkozóan. A jelen témakörben ismertetett funkció a Windows Insider Előzetes verzióelőzetes verzióiban érhető el.
Ez a cikk útmutatót nyújt a teljes körű munkafolyamat megvalósításához a kulcscserék végrehajtásához, a Microsoft CNG API-jával és aLattice-Based kulcsbeágyazási mechanizmus (ML-KEM) algoritmus használatával.
Minta ML-KEM kulcs kapszulázási és dekapszulázási mintakód a BCrypt használatával
ML-KEM egy kulcscseréhez használt poszt-kvantum algoritmus, és a FIPS 203-ban szabványosított. A kulcscsere az olyan biztonsági protokollok fontos része, mint a TLS, amelynek során az ügyfél és a kiszolgáló egyeztet egy kapcsolatot, és kulcsanyagokat hoz létre és oszt meg az interneten keresztül küldött üzenetek titkosításához és visszafejtéséhez. A KEM-ben a kulcspárok generálási folyamatában előállított kulcsokat kapszulázó kulcsnak és dekapszuláló kulcsnak nevezik. A beágyazási kulcs nyilvános, és bárki használhatja kulcsbefoglalási művelet végrehajtására, amely titkos kulcsot (a műveletet végrehajtó fél számára) és egy rejtjelszöveget hoz létre. A titkosítási szöveget a titkos kulcs tulajdonosa adja meg a kulcsfejlelési művelet bemeneteként, hogy helyreállítsa azt a közös titkos kulcsot, amelyet a beágyazó fél a kulcsbefoglalási folyamat során szerzett be. Ez a példa bemutatja, hogy egy hipotetikus TLS-ügyfél- és kiszolgálóalkalmazás hogyan használja fel az új ML-KEM API-kat a BCryptben a kulcscsere végrehajtásához.
Beállítás és kulcspárok létrehozása
Az alábbi lépések ismertetik a ML-KEM kulcspárok létrehozásának és beágyazásának folyamatát:
A BCryptGenerateKeyPair és a BCRYPT_MLKEM_ALG_HANDLE használatával hozzon létre egy új kulcspárt a kulcsok beágyazásához. A hosszúság és a zászlók mezők egyaránt
0
, mivel a kulcshosszokat a ML-KEM paraméterkészlet határozza meg.// Generate the key pair for key exchange unique_bcrypt_key hKeyPair; THROW_IF_NTSTATUS_FAILED( BCryptGenerateKeyPair( BCRYPT_MLKEM_ALG_HANDLE, &hKeyPair, 0, // dwLength 0)); // dwFlags
Hívja meg a BCryptSetProperty függvényt a kulcspáron, hogy beállítsa a BCRYPT_PARAMETER_SET_NAME értékét a BCRYPT_MLKEM_PARAMETER_SET_768 értékre, így meghatározva a ML-KEM művelet paraméterkészletét. ML-KEM az NIST által definiált 512- és 1024-s paraméterkészleteket is támogatja.
THROW_IF_NTSTATUS_FAILED( BCryptSetProperty( &hKeyPair, BCRYPT_PARAMETER_SET_NAME, (PUCHAR) BCRYPT_MLKEM_PARAMETER_SET_768, sizeof(BCRYPT_MLKEM_PARAMETER_SET_768), 0)); // dwFlags
Hívja meg a BCryptFinalizeKeyPair parancsot, hogy a kulcspár készen álljon a későbbi műveletekben való használatra.
THROW_IF_NTSTATUS_FAILED( BCryptFinalizeKeyPair( hKeyPair.get(), 0)); // dwFlags
Nyilvános kulcs exportálása és cseréje
A BCryptExportKey hívása kimeneti
NULL
pufferrel a BCRYPT_MLKEM_ENCAPSULATION_BLOB exportálásához szükséges méret lekérdezéséhez.ULONG cbEncapsulationKeyBlob = 0; THROW_IF_NTSTATUS_FAILED( BCryptExportKey( hKeyPair.get(), NULL, BCRYPT_MLKEM_ENCAPSULATION_BLOB, NULL, // pbOutput 0, // cbOutput &cbEncapsulationKeyBlob, 0)); // dwFlags
Foglaljon le egy puffert a korábban lekért méret alapján, majd exportálja a beágyazási (nyilvános) kulcsot a BCryptExportKey használatával. Ezt a blobot a rendszer elküldi a kulcscserepartnernek (például egy ügyfél-kiszolgáló forgatókönyvben lévő kiszolgálónak).
vector<BYTE> encapsulationKeyBlob(cbEncapsulationKeyBlob); THROW_IF_NTSTATUS_FAILED( BCryptExportKey( hKeyPair.get(), NULL, BCRYPT_MLKEM_ENCAPSULATION_BLOB, encapsulationKeyBlob.data(), static_cast<ULONG>(encapsulationKeyBlob.size()), &cbEncapsulationKeyBlob, 0));
Győződjön meg arról, hogy a BCRYPT_MLKEM_KEY_BLOB rendelkezik a megfelelő nyilvános varázslattal és 768 paraméterkészlettel.
BCRYPT_MLKEM_KEY_BLOB* pEncapsulationKeyBlob = reinterpret_cast<BCRYPT_MLKEM_KEY_BLOB *>(encapsulationKeyBlob.data()); ASSERT(pEncapsulationKeyBlob->dwMagic == BCRYPT_MLKEM_PUBLIC_MAGIC); ASSERT(pEncapsulationKeyBlob->cbParameterSet == sizeof(BCRYPT_MLKEM_PARAMETER_SET_768)); if (wcscmp(BCRYPT_MLKEM_PARAMETER_SET_768, reinterpret_cast<WCHAR *>(pEncapsulationKeyBlob + 1)) != 0) { return; }
Küldje el a beágyazási kulcsot a kiszolgálónak az ügyfélkulcs-csereüzenetben.
PBYTE pbEncapsulationKey = reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768); ULONG cbEncapsulationKey = pEncapsulationKeyBlob->cbKey; SendToServer(pbEncapsulationKey, cbEncapsulationKey);
Beágyazás és Kibontás
Az alábbi lépések a megosztott titkos kulcs beágyazásának és lefejezésének folyamatát ismertetik:
A kiszolgáló megkapja az ügyfél kulcscsere üzenetét, és lekéri a beágyazási kulcs bájtjait.
// Server receives the client's key_exchange message and retrieves the // encapsulation key bytes. vector<BYTE> encapsulationKey = GetClientKeyExchange(); ULONG cbEncapsulationKey = static_cast<ULONG>(encapsulationKey.size());
A kiszolgáló a kulcsot a 768 paraméterkészlet és egy nyilvános varázskód használatával egy BCRYPT_KEY_BLOB helyezi el, majd importálja a beágyazási kulcsot.
// Put the Key in a BCRYPT_KEY_BLOB and import it. ULONG cbEncapsulationKeyBlob = sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768) + cbEncapsulationKey; vector<BYTE> encapsulationKeyBlob(cbEncapsulationKeyBlob); BCRYPT_MLKEM_KEY_BLOB* pEncapsulationKeyBlob = reinterpret_cast<BCRYPT_MLKEM_KEY_BLOB *>(encapsulationKeyBlob.data()); pEncapsulationKeyBlob->dwMagic = BCRYPT_MLKEM_PUBLIC_MAGIC; pEncapsulationKeyBlob->cbParameterSet = sizeof(BCRYPT_MLKEM_PARAMETER_SET_768); pEncapsulationKeyBlob->cbKey = cbEncapsulationKey; CopyMemory( reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB), BCRYPT_MLKEM_PARAMETER_SET_768, sizeof(BCRYPT_MLKEM_PARAMETER_SET_768)); CopyMemory( reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768), encapsulationKey.data(), encapsulationKey.size()); unique_bcrypt_key hKeyPair; // The server knows the ML-KEM parameter set from the client's // key_exchange, which denotes the parameter set associated with the // encapsulation key it sent. In this case, we know it's // BCRYPT_MLKEM_PARAMETER_SET_768. THROW_IF_NTSTATUS_FAILED( BCryptImportKeyPair( BCRYPT_MLKEM_ALG_HANDLE, NULL, // hImportKey BCRYPT_MLKEM_ENCAPSULATION_BLOB, &hKeyPair, encapsulationKeyBlob.data(), static_cast<ULONG>(encapsulationKeyBlob.size()), 0)); // dwFlags // Get the secret key length and ciphertext length. These values are static // and can be cached for the algorithm handle. ULONG cbSecretKey = 0; ULONG cbProperty = sizeof(cbSecretKey);
A kiszolgáló A BCryptGetProperty használatával lekéri a titkos kulcs hosszát és a rejtjelszöveg hosszát, és lefoglalja a szükséges puffereket mindkettőhöz.
THROW_IF_NTSTATUS_FAILED( BCryptGetProperty( &hKeyPair, BCRYPT_KEM_SHARED_SECRET_LENGTH, reinterpret_cast<PUCHAR>(&cbSecretKey), cbProperty, &cbProperty, 0)); // dwFlags ULONG cbCipherText = 0; cbProperty = sizeof(cbCipherText); THROW_IF_NTSTATUS_FAILED( BCryptGetProperty( &hKeyPair, BCRYPT_KEM_CIPHERTEXT_LENGTH, reinterpret_cast<PUCHAR>(&cbCipherText), cbProperty, &cbProperty, 0)); // dwFlags // Then allocate the required buffers. vector<BYTE> secretKey(cbSecretKey); vector<BYTE> cipherText(cbCipherText);
A kiszolgáló végrehajtja a BCryptEncapsulate parancsot , és elküldi a rejtjelszöveget az ügyfélnek a kiszolgálókulcs-csereüzenetben.
// Perform the encapsulate operation. THROW_IF_NTSTATUS_FAILED( BCryptEncapsulate( hKeyPair.get(), secretKey.data(), static_cast<ULONG>(secretKey.size()), &cbSecretKey, cipherText.data(), static_cast<ULONG>(cipherText.size()), &cbCipherText, 0)); // dwFlags // cipherText is sent to the client in the server's key_exchange message. SendToClient(cipherText.data(), cipherText.size());
Az ügyfél a fogadott kiszolgálókulcs-cserével hoz létre egy titkosítószöveget, amely egy megosztott titkos kulcsot foglal magában.
// pbEncapsulationKey is sent on the wire in the client's key_exchange // message. // ... // < It's now the server's turn. It will use the encapsulation key to // generate the a CipherText encapsulating the shared secret key and send // it as a response to the client's key_exchange message. Sample_Server() // demonstrates how a hypothetical server may do this.> // ... // When the ServerKeyExchange message is received from the TLS server, // get the ML-KEM CipherText from the ServerKeyExchange message. vector<BYTE> cipherText = GetServerKeyExchange();
Az ügyfél meghívja a BCryptGetPropertyt a titkos kulcs hosszának lekéréséhez és a megfelelő puffer lefoglalásához.
// Get the secret key length. This value is static and can be cached for // the algorithm handle. ULONG cbSecretKey = 0; ULONG cbProperty = sizeof(cbSecretKey); THROW_IF_NTSTATUS_FAILED( BCryptGetProperty( &hKeyPair, BCRYPT_KEM_SHARED_SECRET_LENGTH, reinterpret_cast<PUCHAR>(&cbSecretKey), cbProperty, &cbProperty, 0)); // dwFlags
Az ügyfél egy kulcsblob létrehozásával és a BCryptDecapsulate meghívásával hozza létre a megosztott titkos kulcsot a titkosítási szöveggel és a titkos kód hosszával.
vector<BYTE> secretKey(cbSecretKey); THROW_IF_NTSTATUS_FAILED( BCryptDecapsulate( hKeyPair.get(), cipherText.data(), static_cast<ULONG>(cipherText.size()), secretKey.data(), static_cast<ULONG>(secretKey.size()), &cbSecretKey, 0)); // dwFlags
Munkamenetkulcsok származtatása
Az ügyfél és a kiszolgáló is ugyanazzal a megosztott titkos kóddal rendelkezik, amelyet átadhat egy kulcslevezetési függvénynek, például a DeriveSessionKeys-nek , hogy munkamenetkulcsokat hozzon létre a biztonságos kommunikációhoz.
// secretKey contains the shared secret key which plugs into the TLS key
// schedule.
DeriveSessionKeys(secretKey);
A teljes kódminta áttekintése
Az alábbi teljes kódmintát áttekintheti:
void Sample_Client()
{
// Generate the key pair for key exchange
unique_bcrypt_key hKeyPair;
THROW_IF_NTSTATUS_FAILED(
BCryptGenerateKeyPair(
BCRYPT_MLKEM_ALG_HANDLE,
&hKeyPair,
0, // dwLength
0)); // dwFlags
THROW_IF_NTSTATUS_FAILED(
BCryptSetProperty(
&hKeyPair,
BCRYPT_PARAMETER_SET_NAME,
(PUCHAR) BCRYPT_MLKEM_PARAMETER_SET_768,
sizeof(BCRYPT_MLKEM_PARAMETER_SET_768),
0)); // dwFlags
THROW_IF_NTSTATUS_FAILED(
BCryptFinalizeKeyPair(
hKeyPair.get(),
0)); // dwFlags
ULONG cbEncapsulationKeyBlob = 0;
THROW_IF_NTSTATUS_FAILED(
BCryptExportKey(
hKeyPair.get(),
NULL,
BCRYPT_MLKEM_ENCAPSULATION_BLOB,
NULL, // pbOutput
0, // cbOutput
&cbEncapsulationKeyBlob,
0)); // dwFlags
vector<BYTE> encapsulationKeyBlob(cbEncapsulationKeyBlob);
THROW_IF_NTSTATUS_FAILED(
BCryptExportKey(
hKeyPair.get(),
NULL,
BCRYPT_MLKEM_ENCAPSULATION_BLOB,
encapsulationKeyBlob.data(),
static_cast<ULONG>(encapsulationKeyBlob.size()),
&cbEncapsulationKeyBlob,
0));
BCRYPT_MLKEM_KEY_BLOB* pEncapsulationKeyBlob =
reinterpret_cast<BCRYPT_MLKEM_KEY_BLOB *>(encapsulationKeyBlob.data());
ASSERT(pEncapsulationKeyBlob->dwMagic == BCRYPT_MLKEM_PUBLIC_MAGIC);
ASSERT(pEncapsulationKeyBlob->cbParameterSet == sizeof(BCRYPT_MLKEM_PARAMETER_SET_768));
if (wcscmp(BCRYPT_MLKEM_PARAMETER_SET_768, reinterpret_cast<WCHAR *>(pEncapsulationKeyBlob + 1)) != 0)
{
return;
}
PBYTE pbEncapsulationKey = reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768);
ULONG cbEncapsulationKey = pEncapsulationKeyBlob->cbKey;
SendToServer(pbEncapsulationKey, cbEncapsulationKey);
// pbEncapsulationKey is sent on the wire in the client's key_exchange
// message.
// ...
// < It's now the server's turn. It will use the encapsulation key to
// generate the a CipherText encapsulating the shared secret key and send
// it as a response to the client's key_exchange message. Sample_Server()
// demonstrates how a hypothetical server may do this.>
// ...
// When the ServerKeyExchange message is received from the TLS server,
// get the ML-KEM CipherText from the ServerKeyExchange message.
vector<BYTE> cipherText = GetServerKeyExchange();
// Get the secret key length. This value is static and can be cached for
// the algorithm handle.
ULONG cbSecretKey = 0;
ULONG cbProperty = sizeof(cbSecretKey);
THROW_IF_NTSTATUS_FAILED(
BCryptGetProperty(
&hKeyPair,
BCRYPT_KEM_SHARED_SECRET_LENGTH,
reinterpret_cast<PUCHAR>(&cbSecretKey),
cbProperty,
&cbProperty,
0)); // dwFlags
vector<BYTE> secretKey(cbSecretKey);
THROW_IF_NTSTATUS_FAILED(
BCryptDecapsulate(
hKeyPair.get(),
cipherText.data(),
static_cast<ULONG>(cipherText.size()),
secretKey.data(),
static_cast<ULONG>(secretKey.size()),
&cbSecretKey,
0)); // dwFlags
// secretKey is the shared secret key which plugs into the TLS key
// schedule.
DeriveSessionKeys(secretKey);
}
void Sample_Server()
{
// Server receives the client's key_exchange message and retrieves the
// encapsulation key bytes.
vector<BYTE> encapsulationKey = GetClientKeyExchange();
ULONG cbEncapsulationKey = static_cast<ULONG>(encapsulationKey.size());
// Put the Key in a BCRYPT_KEY_BLOB and import it.
ULONG cbEncapsulationKeyBlob = sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768) + cbEncapsulationKey;
vector<BYTE> encapsulationKeyBlob(cbEncapsulationKeyBlob);
BCRYPT_MLKEM_KEY_BLOB* pEncapsulationKeyBlob =
reinterpret_cast<BCRYPT_MLKEM_KEY_BLOB *>(encapsulationKeyBlob.data());
pEncapsulationKeyBlob->dwMagic = BCRYPT_MLKEM_PUBLIC_MAGIC;
pEncapsulationKeyBlob->cbParameterSet = sizeof(BCRYPT_MLKEM_PARAMETER_SET_768);
pEncapsulationKeyBlob->cbKey = cbEncapsulationKey;
CopyMemory(
reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB),
BCRYPT_MLKEM_PARAMETER_SET_768,
sizeof(BCRYPT_MLKEM_PARAMETER_SET_768));
CopyMemory(
reinterpret_cast<PBYTE>(pEncapsulationKeyBlob) + sizeof(BCRYPT_MLKEM_KEY_BLOB) + sizeof(BCRYPT_MLKEM_PARAMETER_SET_768),
encapsulationKey.data(),
encapsulationKey.size());
unique_bcrypt_key hKeyPair;
// The server knows the ML-KEM parameter set from the client's
// key_exchange, which denotes the parameter set associated with the
// encapsulation key it sent. In this case, we know it's
// BCRYPT_MLKEM_PARAMETER_SET_768.
THROW_IF_NTSTATUS_FAILED(
BCryptImportKeyPair(
BCRYPT_MLKEM_ALG_HANDLE,
NULL, // hImportKey
BCRYPT_MLKEM_ENCAPSULATION_BLOB,
&hKeyPair,
encapsulationKeyBlob.data(),
static_cast<ULONG>(encapsulationKeyBlob.size()),
0)); // dwFlags
// Get the secret key length and ciphertext length. These values are static
// and can be cached for the algorithm handle.
ULONG cbSecretKey = 0;
ULONG cbProperty = sizeof(cbSecretKey);
THROW_IF_NTSTATUS_FAILED(
BCryptGetProperty(
&hKeyPair,
BCRYPT_KEM_SHARED_SECRET_LENGTH,
reinterpret_cast<PUCHAR>(&cbSecretKey),
cbProperty,
&cbProperty,
0)); // dwFlags
ULONG cbCipherText = 0;
cbProperty = sizeof(cbCipherText);
THROW_IF_NTSTATUS_FAILED(
BCryptGetProperty(
&hKeyPair,
BCRYPT_KEM_CIPHERTEXT_LENGTH,
reinterpret_cast<PUCHAR>(&cbCipherText),
cbProperty,
&cbProperty,
0)); // dwFlags
// Then allocate the required buffers.
vector<BYTE> secretKey(cbSecretKey);
vector<BYTE> cipherText(cbCipherText);
// Perform the encapsulate operation.
THROW_IF_NTSTATUS_FAILED(
BCryptEncapsulate(
hKeyPair.get(),
secretKey.data(),
static_cast<ULONG>(secretKey.size()),
&cbSecretKey,
cipherText.data(),
static_cast<ULONG>(cipherText.size()),
&cbCipherText,
0)); // dwFlags
// cipherText is sent to the client in the server's key_exchange message.
SendToClient(cipherText.data(), cipherText.size());
// secretKey contains the shared secret key which plugs into the TLS key
// schedule.
DeriveSessionKeys(secretKey);
}