Bagikan melalui


Kunci Diffie-Hellman

Menghasilkan Kunci Diffie-Hellman

Untuk menghasilkan kunci Diffie-Hellman, lakukan langkah-langkah berikut:

  1. Panggil fungsi CryptAcquireContext untuk mendapatkan handel ke Penyedia Kriptografi Diffie-Hellman Microsoft.

  2. Buat kunci baru. Ada dua cara untuk mencapainya—dengan meminta CryptoAPI menghasilkan semua nilai baru untuk G, P, dan X atau dengan menggunakan nilai yang ada untuk G dan P, dan menghasilkan nilai baru untuk X.

    Untuk menghasilkan kunci dengan menghasilkan semua nilai baru

    1. Panggil fungsi CryptGenKey , melewati CALG_DH_SF (simpan dan teruskan) atau CALG_DH_EPHEM (sementara) dalam parameter Algid . Kunci akan dihasilkan menggunakan nilai baru dan acak untuk G dan P, nilai yang baru dihitung untuk X, dan handelnya akan dikembalikan dalam parameter phKey .
    2. Kunci baru sekarang siap digunakan. Nilai G dan P harus dikirim ke penerima bersama dengan kunci (atau dikirim oleh beberapa metode lain) saat melakukan pertukaran kunci.

    Untuk menghasilkan kunci dengan menggunakan nilai yang telah ditentukan sebelumnya untuk G dan P

    1. Panggil CryptGenKey yang melewati CALG_DH_SF (simpan dan teruskan) atau CALG_DH_EPHEM (sementara) dalam parameter Algid dan CRYPT_PREGEN untuk parameter dwFlags . Handel kunci akan dihasilkan dan dikembalikan dalam parameter phKey .
    2. Inisialisasi struktur CRYPT_DATA_BLOB dengan anggota pbData yang diatur ke nilai P. BLOB tidak berisi informasi header dan anggota pbData dalam format little-endian.
    3. Nilai P diatur dengan memanggil fungsi CryptSetKeyParam , meneruskan handel kunci yang diambil pada langkah a dalam parameter hKey , bendera KP_P dalam parameter dwParam , dan pointer ke struktur yang berisi nilai P dalam parameter pbData .
    4. Inisialisasi struktur CRYPT_DATA_BLOB dengan anggota pbData yang diatur ke nilai G. BLOB tidak berisi informasi header dan anggota pbData dalam format little-endian.
    5. Nilai G diatur dengan memanggil fungsi CryptSetKeyParam , meneruskan handel kunci yang diambil pada langkah a dalam parameter hKey , bendera KP_G dalam parameter dwParam , dan penunjuk ke struktur yang berisi nilai G dalam parameter pbData .
    6. Nilai X dihasilkan dengan memanggil fungsi CryptSetKeyParam , meneruskan handel kunci yang diambil pada langkah a dalam parameter hKey , bendera KP_X di parameter dwParam , dan NULL dalam parameter pbData .
    7. Jika semua panggilan fungsi berhasil, kunci umum Diffie-Hellman siap digunakan.
  3. Ketika kunci tidak lagi diperlukan, hancurkan dengan meneruskan handel kunci ke fungsi CryptDestroyKey .

Jika CALG_DH_SF ditentukan dalam prosedur sebelumnya, nilai kunci dipertahankan ke penyimpanan dengan setiap panggilan ke CryptSetKeyParam. Nilai G dan P kemudian dapat diambil dengan menggunakan fungsi CryptGetKeyParam . Beberapa CSP mungkin memiliki nilai G dan P yang dikodekan secara permanen. Dalam hal ini kesalahan NTE_FIXEDPARAMETER akan dikembalikan jika CryptSetKeyParam dipanggil dengan KP_G atau KP_P yang ditentukan dalam parameter dwParam . Jika CryptDestroyKey dipanggil, handel ke kunci dihancurkan, tetapi nilai kunci dipertahankan di CSP. Namun, jika CALG_DH_EPHEM ditentukan, handel ke kunci dihancurkan, dan semua nilai dibersihkan dari CSP.

Bertukar Kunci Diffie-Hellman

Tujuan dari algoritma Diffie-Hellman adalah untuk memungkinkan dua pihak atau lebih untuk membuat dan berbagi kunci sesi rahasia yang identik dengan berbagi informasi melalui jaringan yang tidak aman. Informasi yang dibagikan melalui jaringan adalah dalam bentuk beberapa nilai konstan dan kunci publik Diffie-Hellman. Proses yang digunakan oleh dua pihak pertukaran kunci adalah sebagai berikut:

  • Kedua belah pihak menyetujui parameter Diffie-Hellman yang merupakan bilangan prima (P) dan nomor generator (G).
  • Partai 1 mengirimkan kunci umum Diffie-Hellman ke partai 2.
  • Pihak 2 menghitung kunci sesi rahasia dengan menggunakan informasi yang terkandung dalam kunci privat dan kunci publik pihak 1.
  • Partai 2 mengirimkan kunci umum Diffie-Hellman ke partai 1.
  • Pihak 1 menghitung kunci sesi rahasia dengan menggunakan informasi yang terkandung dalam kunci privat dan kunci publik pihak 2.
  • Kedua belah pihak sekarang memiliki kunci sesi yang sama, yang dapat digunakan untuk mengenkripsi dan mendekripsi data. Langkah-langkah yang diperlukan untuk ini diperlihatkan dalam prosedur berikut.

Menyiapkan kunci umum Diffie-Hellman untuk transmisi

  1. Panggil fungsi CryptAcquireContext untuk mendapatkan handel ke Penyedia Kriptografi Diffie-Hellman Microsoft.
  2. Buat kunci Diffie-Hellman dengan memanggil fungsi CryptGenKey untuk membuat kunci baru, atau dengan memanggil fungsi CryptGetUserKey untuk mengambil kunci yang ada.
  3. Dapatkan ukuran yang diperlukan untuk menahan blob kunci Diffie-Hellman dengan memanggil CryptExportKey, meneruskan NULL untuk parameter pbData . Ukuran yang diperlukan akan dikembalikan dalam pdwDataLen.
  4. Alokasikan memori untuk BLOB kunci.
  5. Buat blob kunci publik Diffie-Hellman dengan memanggil fungsi CryptExportKey , meneruskan PUBLICKEYBLOB di parameter dwBlobType dan handel ke kunci Diffie-Hellman dalam parameter hKey . Panggilan fungsi ini menyebabkan perhitungan nilai kunci publik, (G^X) mod P.
  6. Jika semua panggilan fungsi sebelumnya berhasil, blob kunci umum Diffie-Hellman sekarang siap untuk dikodekan dan ditransmisikan.

Untuk mengimpor kunci umum Diffie-Hellman dan menghitung kunci sesi rahasia

  1. Panggil fungsi CryptAcquireContext untuk mendapatkan handel ke Penyedia Kriptografi Diffie-Hellman Microsoft.
  2. Buat kunci Diffie-Hellman dengan memanggil fungsi CryptGenKey untuk membuat kunci baru, atau dengan memanggil fungsi CryptGetUserKey untuk mengambil kunci yang ada.
  3. Untuk mengimpor kunci publik Diffie-Hellman ke CSP, panggil fungsi CryptImportKey , meneruskan pointer ke BLOB kunci publik dalam parameter pbData , panjang BLOB dalam parameter dwDataLen , dan handel ke kunci Diffie-Hellman dalam parameter hPubKey . Ini menyebabkan perhitungan, (Y^X) mod P, dilakukan, sehingga membuat kunci rahasia bersama dan menyelesaikan pertukaran kunci. Panggilan fungsi ini mengembalikan handel ke kunci sesi baru, rahasia, dalam parameter hKey .
  4. Pada titik ini, Diffie-Hellman yang diimpor berjenis CALG_AGREEDKEY_ANY. Sebelum kunci dapat digunakan, kunci harus dikonversi menjadi jenis kunci sesi. Ini dicapai dengan memanggil fungsi CryptSetKeyParam dengan dwParam diatur ke KP_ALGID dan dengan pbData diatur ke pointer ke nilai ALG_ID yang mewakili kunci sesi, seperti CALG_RC4. Kunci harus dikonversi sebelum menggunakan kunci bersama dalam fungsi CryptEncrypt atau CryptDecrypt . Panggilan yang dilakukan ke salah satu fungsi ini sebelum mengonversi jenis kunci akan gagal.
  5. Kunci sesi rahasia sekarang siap digunakan untuk enkripsi atau dekripsi.
  6. Ketika kunci tidak lagi diperlukan, hancurkan handel kunci dengan memanggil fungsi CryptDestroyKey .

Mengekspor Kunci Privat Diffie-Hellman

Untuk mengekspor kunci privat Diffie-Hellman, lakukan langkah-langkah berikut:

  1. Panggil fungsi CryptAcquireContext untuk mendapatkan handel ke Penyedia Kriptografi Diffie-Hellman Microsoft.
  2. Buat kunci Diffie-Hellman dengan memanggil fungsi CryptGenKey untuk membuat kunci baru, atau dengan memanggil fungsi CryptGetUserKey untuk mengambil kunci yang ada.
  3. Buat blob kunci privat Diffie-Hellman dengan memanggil fungsi CryptExportKey , meneruskan PRIVATEKEYBLOB di parameter dwBlobType dan handel ke kunci Diffie-Hellman dalam parameter hKey .
  4. Ketika handel kunci tidak lagi diperlukan, panggil fungsi CryptDestroyKey untuk menghancurkan handel kunci.

Contoh Kode

Contoh berikut menunjukkan cara membuat, mengekspor, mengimpor, dan menggunakan kunci Diffie-Hellman untuk melakukan pertukaran kunci.

#include <tchar.h>
#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")

// The key size, in bits.
#define DHKEYSIZE 512

// Prime in little-endian format.
static const BYTE g_rgbPrime[] = 
{
    0x91, 0x02, 0xc8, 0x31, 0xee, 0x36, 0x07, 0xec, 
    0xc2, 0x24, 0x37, 0xf8, 0xfb, 0x3d, 0x69, 0x49, 
    0xac, 0x7a, 0xab, 0x32, 0xac, 0xad, 0xe9, 0xc2, 
    0xaf, 0x0e, 0x21, 0xb7, 0xc5, 0x2f, 0x76, 0xd0, 
    0xe5, 0x82, 0x78, 0x0d, 0x4f, 0x32, 0xb8, 0xcb,
    0xf7, 0x0c, 0x8d, 0xfb, 0x3a, 0xd8, 0xc0, 0xea, 
    0xcb, 0x69, 0x68, 0xb0, 0x9b, 0x75, 0x25, 0x3d,
    0xaa, 0x76, 0x22, 0x49, 0x94, 0xa4, 0xf2, 0x8d 
};

// Generator in little-endian format.
static BYTE g_rgbGenerator[] = 
{
    0x02, 0x88, 0xd7, 0xe6, 0x53, 0xaf, 0x72, 0xc5,
    0x8c, 0x08, 0x4b, 0x46, 0x6f, 0x9f, 0x2e, 0xc4,
    0x9c, 0x5c, 0x92, 0x21, 0x95, 0xb7, 0xe5, 0x58, 
    0xbf, 0xba, 0x24, 0xfa, 0xe5, 0x9d, 0xcb, 0x71, 
    0x2e, 0x2c, 0xce, 0x99, 0xf3, 0x10, 0xff, 0x3b,
    0xcb, 0xef, 0x6c, 0x95, 0x22, 0x55, 0x9d, 0x29,
    0x00, 0xb5, 0x4c, 0x5b, 0xa5, 0x63, 0x31, 0x41,
    0x13, 0x0a, 0xea, 0x39, 0x78, 0x02, 0x6d, 0x62
};

BYTE g_rgbData[] = {0x01, 0x02, 0x03, 0x04,    0x05, 0x06, 0x07, 0x08};

int _tmain(int argc, _TCHAR* argv[])
{
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);
    
    BOOL fReturn;
    HCRYPTPROV hProvParty1 = NULL; 
    HCRYPTPROV hProvParty2 = NULL; 
    DATA_BLOB P;
    DATA_BLOB G;
    HCRYPTKEY hPrivateKey1 = NULL;
    HCRYPTKEY hPrivateKey2 = NULL;
    PBYTE pbKeyBlob1 = NULL;
    PBYTE pbKeyBlob2 = NULL;
    HCRYPTKEY hSessionKey1 = NULL;
    HCRYPTKEY hSessionKey2 = NULL;
    PBYTE pbData = NULL;

    /************************
    Construct data BLOBs for the prime and generator. The P and G 
    values, represented by the g_rgbPrime and g_rgbGenerator arrays 
    respectively, are shared values that have been agreed to by both 
    parties.
    ************************/
    P.cbData = DHKEYSIZE/8;
    P.pbData = (BYTE*)(g_rgbPrime);

    G.cbData = DHKEYSIZE/8;
    G.pbData = (BYTE*)(g_rgbGenerator);

    /************************
    Create the private Diffie-Hellman key for party 1. 
    ************************/
    // Acquire a provider handle for party 1.
    fReturn = CryptAcquireContext(
        &hProvParty1, 
        NULL,
        MS_ENH_DSS_DH_PROV,
        PROV_DSS_DH, 
        CRYPT_VERIFYCONTEXT);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Create an ephemeral private key for party 1.
    fReturn = CryptGenKey(
        hProvParty1, 
        CALG_DH_EPHEM, 
        DHKEYSIZE << 16 | CRYPT_EXPORTABLE | CRYPT_PREGEN,
        &hPrivateKey1);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Set the prime for party 1's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey1,
        KP_P,
        (PBYTE)&P,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Set the generator for party 1's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey1,
        KP_G,
        (PBYTE)&G,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Generate the secret values for party 1's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey1,
        KP_X,
        NULL,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Create the private Diffie-Hellman key for party 2. 
    ************************/
    // Acquire a provider handle for party 2.
    fReturn = CryptAcquireContext(
        &hProvParty2, 
        NULL,
        MS_ENH_DSS_DH_PROV,
        PROV_DSS_DH, 
        CRYPT_VERIFYCONTEXT);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Create an ephemeral private key for party 2.
    fReturn = CryptGenKey(
        hProvParty2, 
        CALG_DH_EPHEM, 
        DHKEYSIZE << 16 | CRYPT_EXPORTABLE | CRYPT_PREGEN,
        &hPrivateKey2);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Set the prime for party 2's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey2,
        KP_P,
        (PBYTE)&P,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Set the generator for party 2's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey2,
        KP_G,
        (PBYTE)&G,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Generate the secret values for party 2's private key.
    fReturn = CryptSetKeyParam(
        hPrivateKey2,
        KP_X,
        NULL,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Export Party 1's public key.
    ************************/
    // Public key value, (G^X) mod P is calculated.
    DWORD dwDataLen1;

    // Get the size for the key BLOB.
    fReturn = CryptExportKey(
        hPrivateKey1,
        NULL,
        PUBLICKEYBLOB,
        0,
        NULL,
        &dwDataLen1);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Allocate the memory for the key BLOB.
    if(!(pbKeyBlob1 = (PBYTE)malloc(dwDataLen1)))
    { 
        goto ErrorExit;
    }

    // Get the key BLOB.
    fReturn = CryptExportKey(
        hPrivateKey1,
        0,
        PUBLICKEYBLOB,
        0,
        pbKeyBlob1,
        &dwDataLen1);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Export Party 2's public key.
    ************************/
    // Public key value, (G^X) mod P is calculated.
    DWORD dwDataLen2;

    // Get the size for the key BLOB.
    fReturn = CryptExportKey(
        hPrivateKey2,
        NULL,
        PUBLICKEYBLOB,
        0,
        NULL,
        &dwDataLen2);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Allocate the memory for the key BLOB.
    if(!(pbKeyBlob2 = (PBYTE)malloc(dwDataLen2)))
    { 
        goto ErrorExit;
    }

    // Get the key BLOB.
    fReturn = CryptExportKey(
        hPrivateKey2,
        0,
        PUBLICKEYBLOB,
        0,
        pbKeyBlob2,
        &dwDataLen2);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Party 1 imports party 2's public key.
    The imported key will contain the new shared secret 
    key (Y^X) mod P. 
    ************************/
    fReturn = CryptImportKey(
        hProvParty1,
        pbKeyBlob2,
        dwDataLen2,
        hPrivateKey1,
        0,
        &hSessionKey2);
    if(!fReturn)
    {
        goto ErrorExit;
    }
    
    /************************
    Party 2 imports party 1's public key.
    The imported key will contain the new shared secret 
    key (Y^X) mod P. 
    ************************/
    fReturn = CryptImportKey(
        hProvParty2,
        pbKeyBlob1,
        dwDataLen1,
        hPrivateKey2,
        0,
        &hSessionKey1);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Convert the agreed keys to symmetric keys. They are currently of 
    the form CALG_AGREEDKEY_ANY. Convert them to CALG_RC4.
    ************************/
    ALG_ID Algid = CALG_RC4;

    // Enable the party 1 public session key for use by setting the 
    // ALGID.
    fReturn = CryptSetKeyParam(
        hSessionKey1,
        KP_ALGID,
        (PBYTE)&Algid,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Enable the party 2 public session key for use by setting the 
    // ALGID.
    fReturn = CryptSetKeyParam(
        hSessionKey2,
        KP_ALGID,
        (PBYTE)&Algid,
        0);
    if(!fReturn)
    {
        goto ErrorExit;
    }

    /************************
    Encrypt some data with party 1's session key. 
    ************************/
    // Get the size.
    DWORD dwLength = sizeof(g_rgbData);
    fReturn = CryptEncrypt(
        hSessionKey1, 
        0, 
        TRUE,
        0, 
        NULL, 
        &dwLength,
        sizeof(g_rgbData));
    if(!fReturn)
    {
        goto ErrorExit;
    }

    // Allocate a buffer to hold the encrypted data.
    pbData = (PBYTE)malloc(dwLength);
    if(!pbData)
    {
        goto ErrorExit;
    }

    // Copy the unencrypted data to the buffer. The data will be 
    // encrypted in place.
    memcpy(pbData, g_rgbData, sizeof(g_rgbData)); 

    // Encrypt the data.
    dwLength = sizeof(g_rgbData);
    fReturn = CryptEncrypt(
        hSessionKey1, 
        0, 
        TRUE,
        0, 
        pbData, 
        &dwLength,
        sizeof(g_rgbData));
    if(!fReturn)
    {
        goto ErrorExit;
    }
  
    /************************
    Decrypt the data with party 2's session key. 
    ************************/
    dwLength = sizeof(g_rgbData);
    fReturn = CryptDecrypt(
        hSessionKey2,
        0,
        TRUE,
        0,
        pbData,
        &dwLength);
    if(!fReturn)
    {
        goto ErrorExit;
    }


ErrorExit:
    if(pbData)
    {
        free(pbData);
        pbData = NULL;
    }

    if(hSessionKey2)
    {
        CryptDestroyKey(hSessionKey2);
        hSessionKey2 = NULL;
    }

    if(hSessionKey1)
    {
        CryptDestroyKey(hSessionKey1);
        hSessionKey1 = NULL;
    }

    if(pbKeyBlob2)
    {
        free(pbKeyBlob2);
        pbKeyBlob2 = NULL;
    }

    if(pbKeyBlob1)
    {
        free(pbKeyBlob1);
        pbKeyBlob1 = NULL;
    }

    if(hPrivateKey2)
    {
        CryptDestroyKey(hPrivateKey2);
        hPrivateKey2 = NULL;
    }

    if(hPrivateKey1)
    {
        CryptDestroyKey(hPrivateKey1);
        hPrivateKey1 = NULL;
    }

    if(hProvParty2)
    {
        CryptReleaseContext(hProvParty2, 0);
        hProvParty2 = NULL;
    }

    if(hProvParty1)
    {
        CryptReleaseContext(hProvParty1, 0);
        hProvParty1 = NULL;
    }

    return 0;
}