2015-12-19 76 views
16

我目前正在使用C++編寫的項目,該項目利用CryptoAPI執行Diffie-Hellman密鑰交換。由於我最終得到的RC4會話密鑰不能用於加密Python中的相同文本(使用pycrypto),所以我有一些麻煩讓它工作。Diffie-Hellman(與RC4)與Python的Wincrypt

的C++代碼來執行Diffie-Hellman密鑰交換從msdn拍攝,但包括在這裏爲後人:

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

我相信我能完成在Python的Diffie-Hellman密鑰交換,如我可以無誤地生成相同的公鑰和私鑰。我基於我的Diffie-Hellman密鑰交換this repository

我還沒有能夠測試這個,但因爲我似乎無法得到從C++代碼導出的共享密鑰(類似於this thread,這是從來沒有令人滿意的答案)。但是我可以用下面的代碼的RC4會話密鑰:

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

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

,此函數的輸出得到我:

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 

我知道有一個12字節的頭+長這一點,使讓我用下面的16字節RC4會話密鑰:

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

所以我目前正在驗證,我可以利用我從CryptExportKey取得的RC4加密相同的明文。我目前正在加密從上面的C++代碼,它被設置爲g_rgbData

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

用C++代碼中,我得到以下加密輸出:

cc 94 aa ec 86 6e a8 26 

使用pycrypto我有以下代碼:

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') 

這導致下面的輸出:

00 5b 64 25 4e a5 62 e3 

這與C++輸出不匹配。我已經玩弄了內情,但我懷疑還有其他事情可能會發生。

很抱歉,如果這是長篇大論,但它引出了我兩個問題:

  1. 每當你從共享密鑰RC4過渡(使用CryptSetKeyParamCALG_RC4),什麼是下實際回事這裏罩?我似乎無法在任何地方找到有關此過程的任何信息,以便我可以在Python中實現它。

  2. 任何想法爲什麼我的RC4不能在Python中使用相同的密鑰和相同的純文本?

任何幫助將不勝感激!

+1

你可能不應該使用RC4--它被認爲完全破壞(https://tools.ietf.org/html/rfc7465,http://www.theregister.co.uk/2015/07/16/rc4_get_rid_of_it_already_say_boffins /) –

+0

@AlanStokes是的,我一直在閱讀關於RC4相當廣泛的問題來解決這個問題。此代碼不會用於任何生產系統。它起初是爲了學習一些關於密碼學的興趣,RC4看起來非常簡單。現在我試圖理清爲什麼CryptoAPI不像我預期的那樣行事;更多的是一種好奇心。 – Jeremy

+0

這就是爲什麼我們不能有很好的安全性。不妨使用凱撒密碼。 – zaph

回答

6

終於有一段時間來看看你的代碼。當我在本地運行你的代碼時,我可以導出會話密鑰,並可以在pycrypto中成功使用它。我的猜測是,您要麼沒有正確導出會話密鑰(例如,您發佈的是您正在運行的內容?),或者您在C++中加密的數據與您在Python中進行加密的數據不同,請仔細檢查數據你正在加密也是正確的。我懷疑這可能是後者,因爲你已經發布的CryptExportKey並沒有太多。

+0

呃。是的,我不小心刪除了'memcpy(pbData,g_rgbData,sizeof(g_rgbData)); '這讓我加密了我沒有想到的數據。超級煩人,但它現在工作。謝謝! – Jeremy

1

按照PyCrypto docs您的密鑰必須至少爲40個字節:

鍵(字節字符串) - 的祕密密鑰在對稱密碼使用。它可以有任何長度,至少40字節。其密碼強度始終限制在2048位(256字節)。

但隨後會產生矛盾略高於:

key_size = x範圍(1,257)

的密鑰的大小(字節)

允許鍵 - 長度爲1-256,所以我不確定這是否有幫助。