Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Uwaga / Notatka
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed jego wydaniem komercyjnym. Firma Microsoft nie udziela żadnych gwarancji, wyraźnych ani domniemanych, w odniesieniu do podanych tutaj informacji. Funkcja opisana w tym temacie jest dostępna w wersjach wstępnych programu Windows Insider Preview.
Ten artykuł zawiera przewodnik dotyczący implementowania kompleksowego przepływu pracy na potrzeby tworzenia i weryfikowania podpisów cyfrowych przy użyciu algorytmu ML-DSA z interfejsem API CNG firmy Microsoft.
Przykładowy kod ML-DSA przy użyciu narzędzia BCrypt
Dowiedz się, jak używać algorytmu ML-DSA z interfejsem API CNG firmy Microsoft na potrzeby podpisów cyfrowych. Przykładowy kod BCrypt zawiera funkcje podpisywania komunikatu i weryfikowania podpisu, a także eksportowania klucza publicznego. Kod został zaprojektowany tak, aby był łatwy do naśladowania i zrozumiały, co ułatwia deweloperom implementowanie podpisów cyfrowych post-kwantowych w swoich aplikacjach.
Generowanie i weryfikacja podpisu przy użyciu ML-DSA
Ten przykładowy kod przedstawia kompleksowe przepływy pracy tworzenia i weryfikowania podpisów cyfrowych przy użyciu algorytmu ML-DSA z interfejsem API CNG firmy Microsoft. Podkreśla znaczenie dopasowywania kontekstu podczas podpisywania i weryfikacji, a także starannego zarządzania uchwytami kryptograficznymi i pamięcią. Algorytmy podpisu cyfrowego po kwantach są równoważne na wysokim poziomie dla istniejących algorytmów podpisu cyfrowego, których używamy dzisiaj (RSA-PSS, ECDSA itp.), gdzie jedna ze stron generuje parę kluczy publicznych/prywatnych i podpisuje komunikat (lub jego wartość skrótu) przy użyciu klucza prywatnego w celu utworzenia podpisu. Podpis można zweryfikować przez osoby, które mają skojarzony klucz publiczny. Jedną z różnic jest to, że algorytmy podpisu cyfrowego PQ obsługują warianty wstępnego skrótu, które następnie podpisują dane podobne do tradycyjnych algorytmów podpisu, a także czystych wariantów, które podpisują dowolne dane wejściowe o dowolnej długości. W tym przykładzie użyto czystego wariantu ML-DSA i opisano sposób używania pre-hash ML-DSA. Wykonując te kroki, deweloperzy mogą implementować bezpieczne podpisy cyfrowe po kwantach w swoich aplikacjach.
Konfigurowanie uchwytów algorytmów i par kluczy
Wykonaj następujące kroki, aby skonfigurować uchwyty algorytmu i pary kluczy dla ML-DSA:
Użyj elementu BCryptOpenAlgorithmProvider dla algorytmu ML-DSA, wybierając domyślnego dostawcę pierwotnego firmy Microsoft lub innego dostawcę HSM. Ten krok konfiguruje kontekst kryptograficzny dla kolejnych operacji.
status = BCryptOpenAlgorithmProvider( &hAlg, BCRYPT_MLDSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Użyj elementu BCryptGenerateKeyPair , aby utworzyć klucze prywatne i publiczne dla wybranego algorytmu.
status = BCryptGenerateKeyPair(hAlg, &hKeyPair, 0, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Parametry są następujące:
- hAlg: uchwyt dla dostawcy algorytmu, który został uzyskany z BCryptOpenAlgorithmProvider.
- hKeyPair: uchwyt dla pary kluczy.
-
0: wskazuje domyślny rozmiar klucza ml-DSA. -
NULL: dla tej operacji nie ustawiono żadnych flag.
Użyj BCryptSetProperty , aby określić zestaw parametrów do użycia dla ML-DSA, które mają kompromisy w zakresie siły i wydajności. W tym przykładzie wybrano zestaw parametrów 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; }Ustawienie BCRYPT_PARAMETER_SET_NAME wskazuje, który parametr ma być używany (np. ML-DSA-44).
Użyj protokołu BCryptFinalizeKeyPair , aby klucz publiczny i prywatny był gotowy do użycia.
status = BCryptFinalizeKeyPair(hKeyPair, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } // Public/Private key pair is generated at this point
Podpisywanie wiadomości
Aby podpisać komunikat przy użyciu wygenerowanej pary kluczy, kod używa BCryptSignHash.
Kod najpierw określa rozmiar buforu potrzebnego do podpisu, wywołując funkcję BCryptSignHash z
NULLjako wyjściem. Następnie przydziela pamięć dla podpisu.// 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; }Następnie konfiguruje strukturę BCRYPT_PQDSA_PADDING_INFO :
// 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;Struktura BCRYPT_PQDSA_PADDING_INFO zawiera następujące pola:
- pbCtx i cbCtx: wskaźnik i rozmiar opcjonalnego ciągu kontekstu (dodatkowe uwierzytelnione dane).
-
pszPrehashAlgId: ustaw wartość na
NULLdla "czystego" podpisywania (wiadomość jest podpisana bezpośrednio, a nie skrót).
Na koniec rzeczywisty podpis jest generowany przy użyciu innego wywołania do funkcji BCryptSignHash. Do wywołania przekazuje się uchwyt klucza, informacje o wypełnieniu, wiadomość i przydzielony bufor sygnatury. Flaga BCRYPT_PAD_PQDSA określa wypełnienie PQDSA.
status = BCryptSignHash( hKeyPair, &padinfo, (PUCHAR)msg, sizeof(msg), pbSignature, cbSignature, &cbWritten, BCRYPT_PAD_PQDSA); if (!NT_SUCCESS(status)) { goto cleanup; }
Eksportowanie klucza publicznego
W tej sekcji klucz publiczny jest eksportowany, aby inni mogli zweryfikować podpisy.
Klucz BCryptExportKey jest najpierw wywoływany z parametrami
NULLi formatem BCRYPT_PQDSA_PUBLIC_BLOB w celu uzyskania wymaganego rozmiaru buforu:// Get the public key blob size status = BCryptExportKey( hKeyPair, NULL, BCRYPT_PQDSA_PUBLIC_BLOB, NULL, 0, &cbPublicKeyBlob, NULL); if (!NT_SUCCESS(status)) { goto cleanup; }Bufor jest alokowany, a klucz BCryptExportKey jest wywoływany ponownie w celu wyeksportowania klucza publicznego:
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; }
Wyeksportowany klucz jest w formacie BCRYPT_PQDSA_PUBLIC_BLOB .
Uprzątnij zasoby
W kroku czyszczenia przydzielone zasoby, takie jak bufory i uchwyty, są zwalniane, zapewniając brak przecieków pamięci ani porzuconych uchwytów.
cleanup:
if (pbPublicKeyBlob)
{
LocalFree(pbPublicKeyBlob);
}
if (pbSignature)
{
LocalFree(pbSignature);
}
if (hKeyPair != NULL)
{
BCryptDestroyKey(hKeyPair);
}
if (hAlg != NULL)
{
BCryptCloseAlgorithmProvider(hAlg, NULL);
}
Weryfikowanie podpisu
Aby zweryfikować podpis, kod używa elementu BCryptVerifySignature.
- Funkcja MLDSAVerifySample w poniższym przykładowym kodzie odbiera wyeksportowany obiekt blob klucza publicznego, komunikat, kontekst i podpis.
- BCryptImportKeyPair importuje obiekt blob klucza publicznego, aby utworzyć dojście klucza, które jest dostępne dla interfejsu API.
- Ponownie przygotowuje się BCRYPT_PQDSA_PADDING_INFO wraz z odpowiednim kontekstem (powinien być zgodny z tym używanym podczas podpisywania). Ustawiane są następujące pola:
- pbCtx/cbCtx (kontekst): służy do dostarczania dodatkowych danych, które mogą pomóc powiązać podpisy z określoną aplikacją lub przypadkiem użycia. Zarówno podpisywanie, jak i weryfikowanie musi używać tego samego kontekstu.
-
pszPrehashAlgId: dla
NULLwartości "pure" ML-DSA. W przypadku wariantów przed skrótem określa identyfikator algorytmu wyznaczania wartości skrótu. - BCRYPT_PAD_PQDSA: określa użycie dopełnienia PQDSA na potrzeby zabezpieczeń po kwantowych.
- Element BCryptVerifySignature jest wywoływany za pomocą dojścia klucza, informacji dopełniania, oryginalnej wiadomości i podpisu. Jeśli funkcja zwróci powodzenie, podpis jest prawidłowy; w przeciwnym razie nie jest.
Powyższe kroki są oznaczone adnotacjami w poniższym przykładzie kodu:
//
// 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: W bloku kod używa elementu BCryptDestroyKey do usunięcia uchwytu klucza z pamięci i BCryptCloseAlgorithmProvider w celu usunięcia uchwytu algorytmu z pamięci.
Zapoznaj się z pełnym przykładem kodu
Poniższy przykładowy kod przedstawia kompletny proces generowania i weryfikowania podpisu cyfrowego przy użyciu algorytmu ML-DSA z interfejsem API CNG firmy Microsoft:
//
// 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;
}