Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Viss information gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här. Funktionen som beskrivs i det här avsnittet är tillgänglig i förhandsversioner av Windows Insider Preview.
Den här artikeln innehåller en guide för att implementera arbetsflödet från slutpunkt till slutpunkt för att skapa och verifiera digitala signaturer med hjälp av ML-DSA-algoritmen med Microsofts CNG API.
Exempel på ML-DSA kod med BCrypt
Lär dig hur du använder ML-DSA-algoritmen med Microsofts CNG API för digitala signaturer. BCrypt-exemplet innehåller funktioner för att signera ett meddelande och verifiera signaturen samt exportera den offentliga nyckeln. Koden är utformad för att vara lätt att följa och förstå, vilket gör den lämplig för utvecklare som vill implementera digitala signaturer efter kvantum i sina program.
Signaturgenerering och verifiering med ML-DSA
Det här kodexemplet visar arbetsflödet från slutpunkt till slutpunkt för att skapa och verifiera digitala signaturer med hjälp av ML-DSA-algoritmen med Microsofts CNG-API. Den belyser vikten av matchande kontext under signering och verifiering, samt noggrann hantering av kryptografiska referenser och minne. Algoritmer för digitala signaturer efter kvantum motsvarar på hög nivå de befintliga algoritmer för digitala signaturer som vi använder idag (RSA-PSS, ECDSA osv.), där en part genererar ett offentligt/privat nyckelpar och signerar ett meddelande (eller dess hashvärde) med den privata nyckeln för att skapa en signatur. Signaturen kan verifieras av alla som har den associerade offentliga nyckeln. En skillnad är att PQ-algoritmer för digitala signaturer stöder pre-hash-varianter, som hash signerar data som liknar traditionella signaturalgoritmer, samt rena varianter, som signerar indata av godtycklig längd. Det här exemplet använder den rena ML-DSA-varianten och beskriver hur du använder ML-DSA före hash. Genom att följa dessa steg kan utvecklare implementera säkra digitala signaturer efter kvantum i sina program.
Konfigurera algoritmhandtag och nyckelpar
Du följer dessa steg för att konfigurera algoritmhandtag och nyckelpar för ML-DSA:
Använd BCryptOpenAlgorithmProvider för ML-DSA-algoritmen och välj antingen microsofts standardprimärprovider eller en annan HSM-provider. Det här steget konfigurerar den kryptografiska kontexten för efterföljande åtgärder.
status = BCryptOpenAlgorithmProvider( &hAlg, BCRYPT_MLDSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Använd BCryptGenerateKeyPair för att skapa privata och offentliga nycklar för den valda algoritmen.
status = BCryptGenerateKeyPair(hAlg, &hKeyPair, 0, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Parametrarna är följande:
- hAlg: Handtaget för algoritmprovidern, som hämtades från BCryptOpenAlgorithmProvider.
- hKeyPair: Handtaget för nyckelparet.
-
0: Anger standardnyckelstorleken för ML-DSA. -
NULL: Inga flaggor har angetts för den här åtgärden.
Använd BCryptSetProperty för att ange vilken parameter som ska användas för ML-DSA, som har kompromisser för styrka och prestanda. I det här exemplet väljs parameteruppsättningen ML-DSA-44.
status = BCryptSetProperty(&hKeyPair, BCRYPT_PARAMETER_SET_NAME, (PUCHAR)BCRYPT_MLDSA_PARAMETER_SET_44, sizeof(BCRYPT_MLDSA_PARAMETER_SET_44), 0); if (!NT_SUCCESS(status)) { goto cleanup; }Inställningen BCRYPT_PARAMETER_SET_NAME anger vilken parameter som ska användas (t.ex. ML-DSA-44).
Använd BCryptFinalizeKeyPair så att den offentliga och privata nyckeln är redo att användas.
status = BCryptFinalizeKeyPair(hKeyPair, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } // Public/Private key pair is generated at this point
Signera meddelandet
Om du vill signera ett meddelande med det genererade nyckelparet använder koden BCryptSignHash.
Koden avgör först den buffertstorlek som krävs för signaturen genom att anropa BCryptSignHash med
NULLutdata. Den allokerar sedan minne för signaturen.// Get the signature size status = BCryptSignHash(hKeyPair, NULL, NULL, 0, NULL, 0, &cbSignature, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } pbSignature = (PBYTE)LocalAlloc(LMEM_FIXED, cbSignature); if (pbSignature == NULL) { status = STATUS_NO_MEMORY; goto cleanup; }Därefter konfigureras en BCRYPT_PQDSA_PADDING_INFO struktur:
// Sign with pure ML-DSA // // Pure variants sign arbitrary length messages whereas // the pre-hash variants sign a hash value of the input. // To sign with pure ML-DSA or pure SLH-DSA, pszPrehashAlgId must be set // to NULL if BCRYPT_PQDSA_PADDING_INFO is provided to the sign/verify // functions. // For pre-hash signing, the hash algorithm used in creating // the hash of the input must be specified using the pszPrehashAlgId // field of BCRYPT_PQDSA_PADDING_INFO. This hash algorithm information // is required in generating and verifying the signature as the OID of // the hash algorithm becomes a prefix of the input to be signed. // padinfo.pbCtx = (PUCHAR)ctx; padinfo.cbCtx = sizeof(ctx); padinfo.pszPrehashAlgId = NULL;Strukturen BCRYPT_PQDSA_PADDING_INFO innehåller följande fält:
- pbCtx och cbCtx: En pekare och storlek för en valfri kontextsträng (ytterligare autentiserade data).
-
pszPrehashAlgId: Ange till
NULLför "ren" signering (meddelandet signeras direkt, inte en hash).
Slutligen genereras den faktiska signaturen med ett annat anrop till BCryptSignHash. Nyckelhandtaget, utfyllnadsinformationen, meddelandet och den allokerade signaturbufferten skickas till anropet. Flaggan BCRYPT_PAD_PQDSA anger PQDSA-utfyllnad.
status = BCryptSignHash( hKeyPair, &padinfo, (PUCHAR)msg, sizeof(msg), pbSignature, cbSignature, &cbWritten, BCRYPT_PAD_PQDSA); if (!NT_SUCCESS(status)) { goto cleanup; }
Exportera den offentliga nyckeln
I det här avsnittet exporteras den offentliga nyckeln så att andra kan verifiera signaturerna.
BCryptExportKey anropas först med
NULLparametrar och BCRYPT_PQDSA_PUBLIC_BLOB format för att få den buffertstorlek som krävs:// Get the public key blob size status = BCryptExportKey( hKeyPair, NULL, BCRYPT_PQDSA_PUBLIC_BLOB, NULL, 0, &cbPublicKeyBlob, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Bufferten allokeras och BCryptExportKey anropas igen för att exportera den offentliga nyckeln:
pbPublicKeyBlob = (PBYTE)LocalAlloc(LMEM_FIXED, cbPublicKeyBlob); if (pbPublicKeyBlob == NULL) { status = STATUS_NO_MEMORY; goto cleanup; } // Export the public key status = BCryptExportKey( hKeyPair, NULL, BCRYPT_PQDSA_PUBLIC_BLOB, pbPublicKeyBlob, cbPublicKeyBlob, &cbWritten, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }
Den exporterade nyckeln är i BCRYPT_PQDSA_PUBLIC_BLOB format.
Rensa resurser
I rensningssteget frigörs allokerade resurser som buffertar och handtag, vilket säkerställer att inga minnesläckor eller dinglande handtag.
cleanup:
if (pbPublicKeyBlob)
{
LocalFree(pbPublicKeyBlob);
}
if (pbSignature)
{
LocalFree(pbSignature);
}
if (hKeyPair != NULL)
{
BCryptDestroyKey(hKeyPair);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
Verifiera signaturen
För att verifiera en signatur använder koden BCryptVerifySignature.
- Funktionen MLDSAVerifySample i exempelkoden nedan tar emot den exporterade offentliga nyckelbloben, meddelandet, kontexten och signaturen.
- BCryptImportKeyPair importerar den offentliga nyckelbloben för att skapa en nyckel som kan användas av API:et.
- Återigen förbereds BCRYPT_PQDSA_PADDING_INFO med kontexten (den bör matcha den som användes under signeringen). Följande fält anges:
- pbCtx/cbCtx (Kontext): Används för att tillhandahålla ytterligare data, vilket kan hjälpa till att binda signaturer till ett specifikt program eller användningsfall. Både signering och verifiering måste använda samma kontext.
-
pszPrehashAlgId: Inställt på
NULLför "ren" ML-DSA. För pre-hash-varianter anger den hash-algoritmens ID. - BCRYPT_PAD_PQDSA: Anger användningen av PQDSA-utfyllnad för post-quantum-säkerhet.
- BCryptVerifySignature anropas med nyckelhandtaget, utfyllnadsinformationen, det ursprungliga meddelandet och signaturen. Om funktionen returnerar framgång är signaturen giltig. annars är det inte det.
Stegen ovan kommenteras i kodexemplet nedan:
//
// This function takes as input an ML-DSA-44 public key blob,
// and a signature generated by the same algorithm along with
// the message the signature belongs to. It verifies whether the
// signature is valid or not.
//
// STEP 1: Receive the public key blob, message, context, and signature
NTSTATUS MLDSAVerifySample(
PCBYTE pbPublicKeyBlob,
ULONG cbPublicKeyBlob,
PCBYTE pbMsg,
ULONG cbMsg,
PCBYTE pbContext,
ULONG cbContext,
PCBYTE pbSignature,
ULONG cbSignature)
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// STEP 2: Import the public key
// Import the public key
status = BCryptImportKeyPair(
hAlg,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
&hKey,
(PUCHAR)pbPublicKeyBlob,
cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// STEP 3: Prepare the padding info
// Verify the signature
// Assuming the signature is generated with pure ML-DSA.
// Otherwise pszPrehashAlgId must be set to the identifier
// of the hash algorithm used in pre-hashing the input.
padinfo.pbCtx = (PUCHAR)pbContext;
padinfo.cbCtx = cbContext;
padinfo.pszPrehashAlgId = NULL;
// STEP 4: Verify the signature
status = BCryptVerifySignature(
hKey,
&padinfo,
(PUCHAR)pbMsg, // pbHash
cbMsg, // cbHash
(PUCHAR)pbSignature,
cbSignature,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (hKey != NULL)
{
BCryptDestroyKey(hKey);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}
cleanup: I blocket använder koden BCryptDestroyKey för att ta bort nyckelhandtaget från minnet och BCryptCloseAlgorithmProvider för att ta bort algoritmhandtaget från minnet.
Granska det fullständiga kodexemplet
Följande kodexempel visar hela processen med att generera och verifiera en digital signatur med hjälp av ML-DSA-algoritmen med Microsofts CNG API:
//
// Sample ML-DSA code
//
// This function creates an ML-DSA-44 private key, signs
// a message with it, and exports the public-key. The receiver
// can use the public key, message, and the signature to verify
// that they are generated by the same party who owns the private key.
//
#define SAMPLE_MESSAGE "message"
#define SAMPLE_CONTEXT "context"
NTSTATUS MLDSASignSample()
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKeyPair = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
PBYTE pbSignature = NULL;
ULONG cbSignature, cbWritten;
const BYTE msg[] = SAMPLE_MESSAGE;
const BYTE ctx[] = SAMPLE_CONTEXT;
PBYTE pbPublicKeyBlob = NULL;
ULONG cbPublicKeyBlob;
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptGenerateKeyPair(hAlg, &hKeyPair, 0, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptSetProperty(&hKeyPair, BCRYPT_PARAMETER_SET_NAME, (PUCHAR)BCRYPT_MLDSA_PARAMETER_SET_44, sizeof(BCRYPT_MLDSA_PARAMETER_SET_44), 0);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
status = BCryptFinalizeKeyPair(hKeyPair, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Public/Private key pair is generated at this point
// Get the signature size
status = BCryptSignHash(hKeyPair, NULL, NULL, 0, NULL, 0, &cbSignature, NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
pbSignature = (PBYTE)LocalAlloc(LMEM_FIXED, cbSignature);
if (pbSignature == NULL) {
status = STATUS_NO_MEMORY;
goto cleanup;
}
//
// Sign with pure ML-DSA
//
// Pure variants sign arbitrary length messages whereas
// the pre-hash variants sign a hash value of the input.
// To sign with pure ML-DSA or pure SLH-DSA, pszPrehashAlgId must be set
// to NULL if BCRYPT_PQDSA_PADDING_INFO is provided to the sign/verify
// functions.
// For pre-hash signing, the hash algorithm used in creating
// the hash of the input must be specified using the pszPrehashAlgId
// field of BCRYPT_PQDSA_PADDING_INFO. This hash algorithm information
// is required in generating and verifying the signature as the OID of
// the hash algorithm becomes a prefix of the input to be signed.
//
padinfo.pbCtx = (PUCHAR)ctx;
padinfo.cbCtx = sizeof(ctx);
padinfo.pszPrehashAlgId = NULL;
status = BCryptSignHash(
hKeyPair,
&padinfo,
(PUCHAR)msg,
sizeof(msg),
pbSignature,
cbSignature,
&cbWritten,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
//
// Export the public key
//
// Get the public key blob size
status = BCryptExportKey(
hKeyPair,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
NULL,
0,
&cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
pbPublicKeyBlob = (PBYTE)LocalAlloc(LMEM_FIXED, cbPublicKeyBlob);
if (pbPublicKeyBlob == NULL) {
status = STATUS_NO_MEMORY;
goto cleanup;
}
// Export the public key
status = BCryptExportKey(
hKeyPair,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
pbPublicKeyBlob,
cbPublicKeyBlob,
&cbWritten,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (pbPublicKeyBlob)
{
LocalFree(pbPublicKeyBlob);
}
if (pbSignature)
{
LocalFree(pbSignature);
}
if (hKeyPair != NULL)
{
BCryptDestroyKey(hKeyPair);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}
//
// This function takes as input an ML-DSA-44 public key blob,
// and a signature generated by the same algorithm along with
// the message the signature belongs to. It verifies whether the
// signature is valid or not.
//
NTSTATUS MLDSAVerifySample(
PCBYTE pbPublicKeyBlob,
ULONG cbPublicKeyBlob,
PCBYTE pbMsg,
ULONG cbMsg,
PCBYTE pbContext,
ULONG cbContext,
PCBYTE pbSignature,
ULONG cbSignature)
{
NTSTATUS status = STATUS_SUCCESS;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_PQDSA_PADDING_INFO padinfo = { 0 };
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_MLDSA_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Import the public key
status = BCryptImportKeyPair(
hAlg,
NULL,
BCRYPT_PQDSA_PUBLIC_BLOB,
&hKey,
(PUCHAR)pbPublicKeyBlob,
cbPublicKeyBlob,
NULL);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
// Verify the signature
// Assuming the signature is generated with pure ML-DSA.
// Otherwise pszPrehashAlgId must be set to the identifier
// of the hash algorithm used in pre-hashing the input.
padinfo.pbCtx = (PUCHAR)pbContext;
padinfo.cbCtx = cbContext;
padinfo.pszPrehashAlgId = NULL;
status = BCryptVerifySignature(
hKey,
&padinfo,
(PUCHAR)pbMsg, // pbHash
cbMsg, // cbHash
(PUCHAR)pbSignature,
cbSignature,
BCRYPT_PAD_PQDSA);
if (!NT_SUCCESS(status)) {
goto cleanup;
}
cleanup:
if (hKey != NULL)
{
BCryptDestroyKey(hKey);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
return status;
}