2015-12-19 28 views
16

Attualmente sto lavorando su un progetto scritto in C++ che sfrutta il CryptoAPI per eseguire uno scambio di chiavi Diffie-Hellman. Sto avendo un po 'di problemi nel far funzionare tutto questo poiché l'eventuale chiave della sessione RC4 che ottengo non può essere utilizzata per crittografare lo stesso testo in Python (usando pycrypto).Diffie-Hellman (in RC4) con Wincrypt From Python

codice

Il C++ per eseguire lo scambio di chiavi Diffie-Hellman è stato preso da msdn, ma è incluso qui per i posteri:

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

Credo che posso completare lo scambio di chiavi Diffie-Hellman in Python, come Posso generare le stesse chiavi pubbliche e private senza errori. Ho basato il mio scambio chiave Diffie-Hellman su this repository.

Non sono stato in grado di testarlo, tuttavia, poiché non riesco a ottenere il segreto condiviso esportato dal codice C++ (simile a this thread, la risposta non è mai stata soddisfacente). Posso tuttavia ottenere la chiave di sessione RC4 con il seguente codice:

// Get the key length 
DWORD keylen; 
CryptExportKey( 
    hSessionKey1, 
    NULL,  
    PLAINTEXTKEYBLOB, 
    0, 
    NULL, 
    &keylen); 

// Get the session key 
CryptExportKey( 
    hSessionKey1, 
    NULL,  
    PLAINTEXTKEYBLOB, 
    0, 
    encKey,  
    &keylen); 

L'uscita da questa funzione mi fa:

08 02 00 00 01 68 00 00 10 00 00 00 75 2c 59 8c 6e e0 8c 9f ed 30 17 7e 9d a5 85 2b 

So che c'è un colpo di testa 12 byte + lungo su questo, in modo che mi lascia con la seguente chiave di sessione RC4 16 byte:

75 2c 59 8c 6e e0 8c 9f ed 30 17 7e 9d a5 85 2b 

Così attualmente sto cercando di convalidare che posso cifrare lo stesso testo in chiaro utilizzando la RC4 che ho acquisito dal CryptExportKey. Attualmente sto cercando di crittografare g_rgbData dal codice C++ sopra, che è impostato per:

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

Con il codice C++ ottengo il seguente output cifrato:

cc 94 aa ec 86 6e a8 26 

Utilizzando pycrypto Ho il seguente codice :

from Crypto.Cipher import ARC4 
key = '75 2c 59 8c 6e e0 8c 9f ed 30 17 7e 9d a5 85 2b' 
key = key.replace(' ', '').decode('hex') 

plaintext = '0102030405060708' 
plaintext = plaintext.replace(' ', '').decode('hex') 

rc4 = ARC4.new(key) 
encrypted = rc4.encrypt(plaintext) 

print encrypted.encode('hex') 

Ne risulta la seguente output:

00 5b 64 25 4e a5 62 e3 

Quale non corrisponde all'uscita C++. Ho giocato con endianess, ma sospetto che possa succedere qualcos'altro.

Scusa se questo è lungo senza fiato, ma mi porta alle mie due domande:

  1. Ogni volta che la transizione dalla chiave condivisa per RC4 (utilizzando CryptSetKeyParam con CALG_RC4), ciò che sta realmente accadendo sotto il cappuccio qui? Non riesco a trovare alcuna informazione su questo processo da nessuna parte in modo da poterlo implementare in Python.

  2. Qualche idea sul perché il mio RC4 non funziona con la stessa chiave e lo stesso testo in chiaro in Python?

Qualsiasi aiuto sarebbe molto apprezzato!

+1

Probabilmente non dovresti usare RC4 - è considerato totalmente danneggiato (https://tools.ietf.org/html/rfc7465, http://www.theregister.co.uk/2015/07/16/rc4_get_rid_of_it_already_say_boffins /) –

+0

@AlanStokes Sì, ho letto abbastanza su RC4 abbastanza estensivamente per risolvere questo problema. Questo codice non verrà utilizzato in alcun tipo di sistema di produzione. È iniziato come un interesse nell'apprendere un po 'sulla crittografia, con RC4 che sembra abbastanza semplice. Ora sto cercando di capire perché il CryptoAPI non si comporta come mi aspetto; più una curiosità che altro. – Jeremy

+0

Ecco perché non possiamo avere una buona sicurezza. Potrebbe anche usare un codice Caesar. – zaph

risposta

6

Finalmente ha avuto un po 'di tempo per controllare il codice. Quando eseguo il codice localmente, sono in grado di esportare la chiave di sessione e utilizzarla correttamente in pycrypto. La mia ipotesi è che tu non stia esportando correttamente la chiave di sessione (ad es. È ciò che hai postato su che cosa stai facendo?) O i dati che stai criptando in C++ non sono gli stessi dati che stai crittografando in Python - controlla che i dati stai crittografando è anche corretto. Sospetto che sia probabilmente il secondo, dal momento che non c'è molto che tu possa rovinare con lo CryptExportKey che hai pubblicato.

+0

Ugh. Sì, ho cancellato accidentalmente la 'memcpy (pbData, g_rgbData, sizeof (g_rgbData)); "che mi ha fatto crittografare i dati che non mi aspettavo. Super fastidioso, ma funziona ora. Grazie! – Jeremy

1

Secondo il pycrypto docs la chiave deve essere minimo di 40 byte:

chiave

(stringa di byte) - La chiave segreta da utilizzare nel codice simmetrico. Può avere qualsiasi lunghezza, con un minimo di 40 byte. La sua forza crittografica è sempre limitata a 2048 bit (256 byte).

ma poi c'è una contraddizione appena sopra:

key_size = xrange (1, 257)

dimensione di una chiave (in byte)

permettendo a chiave le lunghezze vanno da 1-256, quindi non sono sicuro se questo aiuta anche.