Condividi tramite


chiavi Diffie-Hellman

Generazione di chiavi Diffie-Hellman

Per generare una chiave di Diffie-Hellman, seguire questa procedura:

  1. Chiamare la funzione CryptAcquireContext per ottenere un handle al provider di crittografia Microsoft Diffie-Hellman.

  2. Generare la nuova chiave. È possibile eseguire questa operazione in due modi: con CryptoAPI generare tutti i nuovi valori per G, P e X o usando i valori esistenti per G e P e generando un nuovo valore per X.

    Per generare la chiave generando tutti i nuovi valori

    1. Chiamare la funzione CryptGenKey , passando CALG_DH_SF (archivio e inoltro) o CALG_DH_EPHEM (effimero) nel parametro Algid . La chiave verrà generata usando nuovi valori casuali per G e P, un valore appena calcolato per X e il relativo handle verrà restituito nel parametro phKey .
    2. La nuova chiave è ora pronta per l'uso. I valori di G e P devono essere inviati al destinatario insieme alla chiave (o inviata da un altro metodo) quando si esegue uno scambio di chiavi.

    Per generare la chiave usando valori predefiniti per G e P

    1. Chiamare CryptGenKey passando CALG_DH_SF (archivio e inoltro) o CALG_DH_EPHEM (effimero) nel parametro AlgideCRYPT_PREGEN per il parametro dwFlags. Un handle chiave verrà generato e restituito nel parametro phKey .
    2. Inizializzare una struttura CRYPT_DATA_BLOB con il membro pbData impostato sul valore P. IL BLOB non contiene informazioni di intestazione e il membro pbData è in formato little-endian .
    3. Il valore di P viene impostato chiamando la funzione CryptSetKeyParam , passando l'handle chiave recuperato nel passaggio di un parametro hKey , il flag KP_P nel parametro dwParam e un puntatore alla struttura che contiene il valore di P nel parametro pbData .
    4. Inizializzare una struttura CRYPT_DATA_BLOB con il membro pbData impostato sul valore G. IL BLOB non contiene informazioni di intestazione e il membro pbData è in formato little-endian.
    5. Il valore di G viene impostato chiamando la funzione CryptSetKeyParam , passando l'handle della chiave recuperato nel passaggio di un parametro hKey , il flag KP_G nel parametro dwParam e un puntatore alla struttura contenente il valore di G nel parametro pbData .
    6. Il valore di X viene generato chiamando la funzione CryptSetKeyParam , passando l'handle della chiave recuperato in un passaggio nel parametro hKey , il flag KP_X nel parametro dwParam e NULL nel parametro pbData .
    7. Se tutte le chiamate di funzione hanno esito positivo, la chiave pubblica Diffie-Hellman è pronta per l'uso.
  3. Quando la chiave non è più necessaria, eliminarla passando l'handle della chiave alla funzione CryptDestroyKey .

Se CALG_DH_SF è stato specificato nelle procedure precedenti, i valori chiave vengono mantenuti nell'archiviazione con ogni chiamata a CryptSetKeyParam. I valori G e P possono quindi essere recuperati usando la funzione CryptGetKeyParam . Alcuni provider di servizi di configurazione possono avere valori G e P hardcoded. In questo caso verrà restituito un errore di NTE_FIXEDPARAMETER se CryptSetKeyParam viene chiamato con KP_G oKP_P specificato nel parametro dwParam. Se viene chiamato CryptDestroyKey , l'handle alla chiave viene eliminato, ma i valori della chiave vengono mantenuti nel CSP. Tuttavia, se CALG_DH_EPHEM è stato specificato, l'handle della chiave viene eliminato e tutti i valori vengono cancellati dal CSP.

Scambio di chiavi Diffie-Hellman

Lo scopo dell'algoritmo Diffie-Hellman consiste nel rendere possibile che due o più parti creino e condividono una chiave di sessione segreta identica condividendo le informazioni su una rete che non è sicura. Le informazioni condivise sulla rete sono sotto forma di un paio di valori costanti e una chiave pubblica Diffie-Hellman. Il processo usato da due parti chiave-scambio è il seguente:

  • Entrambe le parti accettano i parametri Diffie-Hellman che sono un numero primo (P) e un numero di generatore (G).
  • La parte 1 invia la chiave pubblica Diffie-Hellman alla parte 2.
  • La parte 2 calcola la chiave della sessione privata usando le informazioni contenute nella chiave privata e nella chiave pubblica della parte 1.
  • La parte 2 invia la chiave pubblica Diffie-Hellman alla parte 1.
  • La parte 1 calcola la chiave della sessione privata usando le informazioni contenute nella chiave privata e nella chiave pubblica della parte 2.
  • Entrambe le parti hanno ora la stessa chiave di sessione, che può essere usata per crittografare e decrittografare i dati. I passaggi necessari per questo sono illustrati nella procedura seguente.

Per preparare una chiave pubblica Diffie-Hellman per la trasmissione

  1. Chiamare la funzione CryptAcquireContext per ottenere un handle al provider di crittografia Microsoft Diffie-Hellman.
  2. Creare una chiave Diffie-Hellman chiamando la funzione CryptGenKey per creare una nuova chiave oppure chiamando la funzione CryptGetUserKey per recuperare una chiave esistente.
  3. Ottenere le dimensioni necessarie per contenere il BLOB della chiave Diffie-Hellman chiamando CryptExportKey, passando NULL per il parametro pbData . Le dimensioni necessarie verranno restituite in pdwDataLen.
  4. Allocare memoria per il BLOB della chiave.
  5. Creare un BLOB di chiave pubblica Diffie-Hellman chiamando la funzione CryptExportKey , passando PUBLICKEYBLOB nel parametro dwBlobType e l'handle alla chiave Diffie-Hellman nel parametro hKey . Questa chiamata di funzione causa il calcolo del valore della chiave pubblica, (G^X) mod P.
  6. Se tutte le chiamate di funzione precedenti hanno avuto esito positivo, il BLOB della chiave pubblica Diffie-Hellman è ora pronto per essere codificato e trasmesso.

Per importare una chiave pubblica di Diffie-Hellman e calcolare la chiave della sessione privata

  1. Chiamare la funzione CryptAcquireContext per ottenere un handle al provider di crittografia Microsoft Diffie-Hellman.
  2. Creare una chiave Diffie-Hellman chiamando la funzione CryptGenKey per creare una nuova chiave oppure chiamando la funzione CryptGetUserKey per recuperare una chiave esistente.
  3. Per importare la chiave pubblica Diffie-Hellman nel CSP, chiamare la funzione CryptImportKey , passando un puntatore al BLOB della chiave pubblica nel parametro pbData , la lunghezza del BLOB nel parametro dwDataLen e l'handle alla chiave Diffie-Hellman nel parametro hPubKey . In questo modo il calcolo, (Y^X) mod P, deve essere eseguito, creando così la chiave condivisa, privata e completando lo scambio di chiavi. Questa chiamata di funzione restituisce un handle al nuovo, segreto, chiave di sessione nel parametro hKey .
  4. A questo punto, il Diffie-Hellman importato è di tipo CALG_AGREEDKEY_ANY. Prima che la chiave possa essere usata, deve essere convertita in un tipo di chiave sessione. Questa operazione viene eseguita chiamando la funzione CryptSetKeyParam con dwParam impostata su KP_ALGID e con pbData impostata su un puntatore a un valore ALG_ID che rappresenta una chiave di sessione, ad esempio CALG_RC4. La chiave deve essere convertita prima di usare la chiave condivisa nella funzione CryptEncrypt o CryptDecrypt . Le chiamate effettuate a una di queste funzioni prima della conversione del tipo di chiave avranno esito negativo.
  5. La chiave della sessione privata è ora pronta per essere usata per la crittografia o la decrittografia.
  6. Quando la chiave non è più necessaria, eliminare l'handle della chiave chiamando la funzione CryptDestroyKey .

Esportazione di una chiave privata Diffie-Hellman

Per esportare una chiave privata Diffie-Hellman, seguire questa procedura:

  1. Chiamare la funzione CryptAcquireContext per ottenere un handle al provider di crittografia Microsoft Diffie-Hellman.
  2. Creare una chiave Diffie-Hellman chiamando la funzione CryptGenKey per creare una nuova chiave oppure chiamando la funzione CryptGetUserKey per recuperare una chiave esistente.
  3. Creare un BLOB di chiavi private Diffie-Hellman chiamando la funzione CryptExportKey , passando PRIVATEKEYBLOB nel parametro dwBlobType e l'handle alla chiave Diffie-Hellman nel parametro hKey .
  4. Quando l'handle della chiave non è più necessario, chiamare la funzione CryptDestroyKey per eliminare l'handle della chiave.

Codice di esempio

Nell'esempio seguente viene illustrato come creare, esportare, importare e usare una chiave Diffie-Hellman per eseguire uno scambio di chiavi.

#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;
}