Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Önemli
Bu makalede yeni Windows şifreleme uygulamaları için önerilen API olan Cryptography API: Next Generation (CNG) kullanılır. Çoğu yeni uygulama için, daha kısa anahtarlar ve daha az parametre yönetimi yüküyle eşdeğer veya daha güçlü güvenlik sağlayan P-256 veya P-384 gibi standart bir adlandırılmış eğri ile Eliptik Eğri Diffie-Hellman (ECDH) kullanmayı göz önünde bulundurun.
Eski CryptoAPI (CAPI1) işlevleri (CryptGenKey, CryptExportKey, CryptAcquireContextvb.) kullanım dışıdır. Bunları yeni uygulamalarda kullanmayın.
- Diffie-Hellman Anahtarları Oluşturma
- Diffie-Hellman Anahtar Değişimi
- Diffie-Hellman Özel Anahtarını Dışarı Aktarma
- Örnek Kod
- İlgili içerik
Diffie-Hellman Anahtarları Oluşturma
CNG kullanarak Diffie-Hellman anahtar çifti oluşturmak için aşağıdaki adımları gerçekleştirin:
Algoritma sağlayıcı tanıtıcısını almak için BCryptOpenAlgorithmProvider ile
BCRYPT_DH_ALGORITHMçağırın.Anahtar çiftini oluşturmak için BCryptGenerateKeyPair çağrısı yaparak bit cinsinden anahtar boyutunu belirtin. Yeterli güvenlik için en az 2048 bit kullanın; 512 bit anahtarlar (eski CAPI1 örneklerinde kullanıldığı gibi) şifreleme açısından zayıftır ve yeni kodda kullanılmamalıdır.
DH parametrelerini (prime P ve üreteç G) ayarlamak için BCryptSetProperty işlevini
BCRYPT_DH_PARAMETERSözelliğiyle çağırın ve ardından BCryptFinalizeKeyPair işlevini çağırın. Özellik değeri bir BCRYPT_DH_PARAMETER_HEADER yapısı ve hemen ardından P değeri, daha sonra G değeri, her biricbKeyLengthbayt uzunluğunda ve büyük endian bayt sırasına göre olmalıdır.Her iki taraf da aynı P ve G değerlerini kullanmalıdır. Yeni kod için özel parametreler oluşturmak yerine iyi bilinen standartlaştırılmış bir grup kullanın; örneğin RFC 3526'dan 2048 bit MODP Grup 14 (aşağıdaki örnekte kullanılmıştır) iyi bir güvenlik ve uyumluluk dengesi sağlar. Dışarı
BCRYPT_DH_PUBLIC_BLOBaktarma biçimi P ve G'yi içerir, böylece iki taraf ayrı makine veya işlemlerde olduğunda alıcı bunları alınan blobdan ayıklayabilir. Her iki tarafın da aynı işlemi paylaştığı bağımsız bir örnekte, aynı parametre blobu doğrudan yeniden kullanılabilir.Anahtar oluşturmayı tamamlamak için BCryptFinalizeKeyPair'i çağırın. Anahtarın kullanılabilmesi veya dışarı aktarılabilmesi için önce bu işlev çağrılmalıdır.
Anahtar artık gerekli olmadığında, anahtar tanıtıcısını serbest bırakmak için BCryptDestroyKey'i ve sağlayıcı tanıtıcısını serbest bırakmak için BCryptCloseAlgorithmProvider'ı çağırın.
Diffie-Hellman Anahtarları Alışverişi
Diffie-Hellman algoritmasının amacı, iki veya daha fazla tarafın güvenli olmayan bir ağ üzerinden bilgi paylaşarak özdeş, gizli değer oluşturmasını ve paylaşmasını mümkün kılmaktır. Ağ üzerinden paylaşılan bilgiler, her tarafın Diffie-Hellman ortak anahtarıdır. İki anahtar değişimi tarafları tarafından kullanılan süreç aşağıdaki gibidir:
- Her iki taraf da Diffie-Hellman parametreleri üzerinde anlaşır: asal sayı (P) ve jeneratör numarası (G).
- 1. Taraf, Diffie-Hellman ortak anahtarını Parti 2'ye gönderir.
- Taraf 2, paylaşılan gizli anahtarı kendi özel anahtarını ve Taraf 1'in ortak anahtarını kullanarak hesaplar.
- 2. Taraf, Diffie-Hellman ortak anahtarını 1. Taraf'a gönderir.
- Taraf 1, paylaşılan gizli anahtarı kendi özel anahtarını ve Taraf 2'nin açık anahtarını kullanarak hesaplar.
- Her iki taraf da artık simetrik şifreleme anahtarı türetmek için kullanılabilen aynı paylaşılan gizli diziye sahiptir.
Diffie-Hellman ortak anahtarını iletim için hazırlamak için:
Anahtar çiftini oluşturup sonlandırdıktan sonra, blob türü olarak
BCRYPT_DH_PUBLIC_BLOBBCryptExportKey'i çağırarak ortak anahtar baytlarını alın. Blob, P, G ve ortak anahtar Y değerlerinin tümünü büyük endian bayt sırasına göre içerir.Bu baytları ağ üzerinden diğer tarafa iletin.
Uyarı
CNG DH bloblarındaki (BCRYPT_DH_PUBLIC_BLOB, BCRYPT_DH_PRIVATE_BLOB) önemli malzeme büyük endian bayt sırasına göredir. Bu, kullanım dışı bırakılan CryptoAPI (CAPI1) tarafından kullanılan küçük endian biçiminin tersidir. CAPI1 ile kodlanmış anahtar malzemeyle birlikte çalışırken dikkatli olun.
Diffie-Hellman ortak anahtarını içeri aktarmak ve paylaşılan gizli anahtarı türetmek için:
Diğer tarafın ortak anahtarını içeri aktarmak için BCryptImportKeyPair öğesini ile
BCRYPT_DH_PUBLIC_BLOBçağırın. Bunun içinBCRYPT_DH_ALGORITHMile açılmış bir algoritma sağlayıcı tanıtıcısı gerekir.BCryptSecretAgreement'i özel anahtar tutamacınız ve içeri aktarılan ortak anahtar tutamacı ile çağırın. Bu, ham paylaşılan gizli dizi değerini (Y^X) mod P'yi temsil eden bir gizli dizi sözleşmesi tanıtıcısı oluşturur.
Paylaşılan gizli diziden kullanılabilir anahtar malzemesini türetmek için BCryptDeriveKey'i çağırın. Senaryonuza uygun bir anahtar türetme işlevi (KDF) kullanın;
BCRYPT_KDF_HASHileSHA-256, uygun bir genel amaçlı seçimdir.Sonraki şifreleme veya şifre çözme işlemleri için bir simetrik anahtar oluşturmak amacıyla türetilmiş anahtar baytlarını kullanın (örneğin,
BCRYPT_AES_ALGORITHMçağrısı ile).bitirdiğinizde, gizli anlaşma tanıtıcısını serbest bırakmak için BCryptDestroySecret'i ve tüm anahtar tanıtıcılarını serbest bırakmak için BCryptDestroyKey'i çağırın.
Diffie-Hellman Özel Anahtarını Dışarı Aktarma
Dikkat
Özel anahtarları dışarı aktarmak, güvenliğe duyarlı bir işlemdir. Özel anahtar malzemelerini yalnızca kesinlikle gerekli olduğunda dışarı aktarın ve uygun şekilde koruyun. Bir anahtar depolama sağlayıcısında (KSP) depolanan anahtarlar için sağlayıcı, dışarı aktarmayı anahtar ilkesine göre kısıtlayabilir.
Diffie-Hellman özel anahtarı bellek blobu olarak dışarı aktarmak için ile BCRYPT_DH_PRIVATE_BLOB çağırın. Sonuçta elde edilen blob, her biri büyük endian bayt sırasına göre P, G, genel Y ve özel X değerlerinin ardından bir BCRYPT_DH_KEY_BLOB üst bilgisi içerir.
Daha sonra özel anahtarı içeri aktarmak için ile BCRYPT_DH_PRIVATE_BLOB çağırın.
Örnek Kod
Aşağıdaki örnekte, CNG kullanan iki taraf arasında Diffie-Hellman anahtar değişimi gösterilmektedir. Her iki taraf da paylaşılan gizli diziden aynı anahtar malzemeyi türeter ve türetilen baytları karşılaştırır.
Uyarı
Bu örnekte, belirli Diffie-Hellman parametreleri kullanılır ve BCryptDeriveKey ile BCRYPT_KDF_HASH kullanılarak paylaşılan gizli diziden anahtar malzeme türetilir.
Bu örneği uyarlarken parametre biçiminin, anahtar türetme ayarlarının ve sonuçta elde edilen anahtar kullanımının uygulamanızın güvenlik ve birlikte çalışabilirlik gereksinimlerini karşıladığından emin olun.
#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;
}
İlgili içerik
- Şifreleme API'si: Yeni Nesil (CNG)
- Tipik CNG programlamaya genel bakış
- BCryptOpenAlgorithmProvider
- BCryptGenerateKeyPair
- BCryptFinalizeKeyPair
- BCryptSecretAgreement
- BCryptDeriveKey
- BCRYPT_DH_KEY_BLOB
- BCRYPT_DH_PARAMETER_HEADER
- CNG Algoritma Tanımlayıcıları