2017-03-17 104 views
1

我的問題是,我無法從下面的C代碼中獲得AES 256 CTR輸出以匹配下面的OpenSSL命令的輸出。CTR-AES256加密不匹配OpenSSL -aes-256-ctr

C代碼產生這樣的:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 
f9 e4 09 ce 23 26 7b 93 82 02 d3 87 eb 01 26 ac 
96 2c 01 8c c8 af f3 de a4 18 7f 29 46 00 2e 00 

OpenSSL的命令行產生這樣的:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 
3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4 
6f a9 49 bc 6d 00 77 24 2d ef b9 c4 

通知前16個字節是相同的,因爲nonceIV是一樣的,但是,當nonceIV在下一次迭代中更新,然後與明文XOR,接下來的16個字節不同,等等......?

我不明白爲什麼會發生這種情況?任何人都知道爲什麼十六進制代碼在第一個16字節塊之後有所不同?

聲明:我不是C專家。

謝謝!

Fox.txt

The quick brown fox jumped over the lazy dog 

然後運行下面的OpenSSL命令創建foxy.exe

openssl enc -aes-256-ctr -in fox.txt -out foxy.exe -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -iv f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff -nosalt -nopad -p 

這裏是foxy.exe包含:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 
3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4 
6f a9 49 bc 6d 00 77 24 2d ef b9 c4 

他重新的代碼。

#include <Windows.h> 

    // What is AES CTR 
    // 
    // AES - CTR (counter) mode is another popular symmetric encryption algorithm. 
    // 
    // It is advantageous because of a few features : 
    // 1. The data size does not have to be multiple of 16 bytes. 
    // 2. The encryption or decryption for all blocks of the data can happen in parallel, allowing faster implementation. 
    // 3. Encryption and decryption use identical implementation. 
    // 
    // Very important note : choice of initial counter is critical to the security of CTR mode. 
    // The requirement is that the same counter and AES key combination can never to used to encrypt more than more one 16 - byte block. 

    // Notes 
    // ----- 
    // * CTR mode does not require padding to block boundaries. 
    // 
    // * The IV size of AES is 16 bytes. 
    // 
    // * CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. 
    // 
    // * AES is a block cipher : it takes as input a 16 byte plaintext block, 
    // a secret key (16, 24 or 32 bytes) and outputs another 16 byte ciphertext block. 
    // 
    // References 
    // ---------- 
    // https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 
    // https://www.cryptopp.com/wiki/CTR_Mode#Counter_Increment 
    // https://modexp.wordpress.com/2016/03/10/windows-ctr-mode-with-crypto-api/ 
    // https://msdn.microsoft.com/en-us/library/windows/desktop/jj650836(v=vs.85).aspx 
    // http://www.cryptogrium.com/aes-ctr.html 
    // http://www.bierkandt.org/encryption/symmetric_encryption.php 


    #define IV_SIZE 16 
    #define AES_BLOCK_SIZE 16 

    typedef struct _key_hdr_t { 
     PUBLICKEYSTRUC hdr;   // Indicates the type of BLOB and the algorithm that the key uses. 
     DWORD   len;   // The size, in bytes, of the key material. 
     char   key[32];  // The key material. 
    } key_hdr; 


    // NIST specifies two types of counters. 
    // 
    // First is a counter which is made up of a nonce and counter. 
    // The nonce is random, and the remaining bytes are counter bytes (which are incremented). 
    // For example, a 16 byte block cipher might use the high 8 bytes as a nonce, and the low 8 bytes as a counter. 
    // 
    // Second is a counter block, where all bytes are counter bytes and can be incremented as carries are generated. 
    // For example, in a 16 byte block cipher, all 16 bytes are counter bytes. 
    // 
    // This uses the second method, which means the entire byte block is treated as counter bytes. 

    void IncrementCounterByOne(char *inout) 
    { 
     int i; 

     for (i = 16 - 1; i >= 0; i--) { 
      inout[i]++; 
      if (inout[i]) { 
       break; 
      } 
     } 
    } 


    void XOR(char *plaintext, char *ciphertext, int plaintext_len) 
    { 
     int i; 

     for (i = 0; i < plaintext_len; i++) 
     { 
      plaintext[i] ^= ciphertext[i]; 
     } 
    } 


    unsigned int GetAlgorithmIdentifier(unsigned int aeskeylenbits) 
    { 
     switch (aeskeylenbits) 
     { 
     case 128: 
      return CALG_AES_128; 
     case 192: 
      return CALG_AES_192; 
     case 256: 
      return CALG_AES_256; 
     default: 
      return 0; 
     } 
    } 


    unsigned int GetKeyLengthBytes(unsigned int aeskeylenbits) 
    { 
     return aeskeylenbits/8; 
    } 


    void SetKeyData(key_hdr *key, unsigned int aeskeylenbits, char *pKey) 
    { 
     key->hdr.bType = PLAINTEXTKEYBLOB; 
     key->hdr.bVersion = CUR_BLOB_VERSION; 
     key->hdr.reserved = 0; 
     key->hdr.aiKeyAlg = GetAlgorithmIdentifier(aeskeylenbits); 
     key->len = GetKeyLengthBytes(aeskeylenbits); 
     memmove(key->key, pKey, key->len); 
    } 

    // point = pointer to the start of the plaintext, extent is the size (44 bytes) 
    void __stdcall AESCTR(char *point, int extent, char *pKey, char *pIV, unsigned int aeskeylenbits, char *bufOut) 
    { 
     HCRYPTPROV hProv; 
     HCRYPTKEY hSession; 
     key_hdr key; 
     DWORD  IV_len; 
     div_t  aesblocks; 
     char  nonceIV[64]; 
     char  tIV[64]; 
     char  *bufIn; 

     bufIn = point; 

     memmove(nonceIV, pIV, IV_SIZE); 

     SetKeyData(&key, aeskeylenbits, pKey); 

     CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); 

     CryptImportKey(hProv, (PBYTE)&key, sizeof(key), 0, CRYPT_NO_SALT, &hSession); 

     aesblocks = div(extent, AES_BLOCK_SIZE); 

     while (aesblocks.quot != 0) 
     { 
      IV_len = IV_SIZE; 
      memmove(tIV, nonceIV, IV_SIZE); 
      CryptEncrypt(hSession, 0, FALSE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); 
      XOR(bufIn, tIV, AES_BLOCK_SIZE); 
      IncrementCounterByOne(nonceIV); 
      bufIn += AES_BLOCK_SIZE; 
      aesblocks.quot--; 
     } 

     if (aesblocks.rem != 0) 
     { 
      memmove(tIV, nonceIV, IV_SIZE); 
      CryptEncrypt(hSession, 0, TRUE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); 
      XOR(bufIn, tIV, aesblocks.rem); 
     } 

     memmove(bufOut, point, extent); 

     CryptDestroyKey(hSession); 
     CryptReleaseContext(hProv, 0); 
    } 

我能夠通過在M $ CryptEncrypt()建議的僞代碼得到這個工作說明部分https://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx

// Set the IV for the original key. Do not use the original key for 
// encryption or decryption after doing this because the key's 
// feedback register will get modified and you cannot change it. 
CryptSetKeyParam(hOriginalKey, KP_IV, newIV) 

while(block = NextBlock()) 
{ 
    // Create a duplicate of the original key. This causes the 
    // original key's IV to be copied into the duplicate key's 
    // feedback register. 
    hDuplicateKey = CryptDuplicateKey(hOriginalKey) 

    // Encrypt the block with the duplicate key. 
    CryptEncrypt(hDuplicateKey, block) 

    // Destroy the duplicate key. Its feedback register has been 
    // modified by the CryptEncrypt function, so it cannot be used 
    // again. It will be re-duplicated in the next iteration of the 
    // loop. 
    CryptDestroyKey(hDuplicateKey) 
} 

下面是用兩條新線的更新程式碼:

HCRYPTKEY hDuplicateKey; 
boolean final; 

while (aesblocks.quot != 0) 
{ 
    CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey); 
    IV_len = IV_SIZE; 
    memmove(tIV, nonceIV, IV_len); 
    final = (aesblocks.quot == 1 && aesblocks.rem == 0) ? TRUE : FALSE; 
    CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); 
    XOR(bufIn, tIV, AES_BLOCK_SIZE); 
    IncrementCounterByOne(nonceIV); 
    bufIn += AES_BLOCK_SIZE; 
    aesblocks.quot--; 
    CryptDestroyKey(hDuplicateKey); 
} 

if (aesblocks.rem != 0) 
{ 
    CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey); 
    final = TRUE; 
    memmove(tIV, nonceIV, IV_SIZE); 
    CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); 
    XOR(bufIn, tIV, aesblocks.rem); 
    CryptDestroyKey(hDuplicateKey); 
} 
+0

您可能需要[openssl c EVP_BytesToKey](https://www.google.com/search?q=openssl+c+EVP_BytesToKey)。我不記得在WinCrypt(尚未)的上下文中看到關於「EVP_BytesToKey」的問題。 – jww

回答

2

我對Microsoft API不熟悉,但我相信CryptEncrypt()默認使用CBC模式 - 所以第一塊加密的輸出會自動變爲f編入第二個塊的輸入。您正在構建CTR模式,自己形成草稿(順便說一句,通常不是一個明智的做法 - 您應該使用加密庫的功能,而不是「推出自己的」加密)。要獲得預期的輸出,您可能需要使CryptEncrypt在ECB模式下使用AES - 我相信可以使用CryptptSetKeyParam(https://msdn.microsoft.com/en-us/library/aa380272.aspx)並將KP_MODE設置爲CRYPT_MODE_ECB來完成。

+0

感謝您的提示。再次訪問CryptEncrypt()文檔,發現M $暗示了這一點: 「設置原始密鑰的IV。這樣做後,請勿使用原始密鑰進行加密或解密,因爲密鑰的 反饋寄存器將被修改,您無法更改它。「 – vengy

0

確保您的輸入文件不包含任何額外的字符,如新行等Openssl將包括這些額外的字符,同時加密。