2017-03-17 5 views
1

내 문제는 아래의 OpenSSL 명령의 출력과 일치하도록 아래 C 코드에서 AES 256 CTR 출력을 가져올 수 없다는 것입니다.CTR-AES256 암호화가 OpenSSL과 일치하지 않습니다. -aes-256-ctr

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 명령 라인이 생성 :

C 코드는이 생산 nonceIV했기 때문에 상기 제 16 바이트가 동일한

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 

공지 동일하지만 때 nonceIV가 다음 반복에서 업데이트 된 다음 일반 텍스트와 XOR 된 다음 16 바이트가 달라지는 등 ...?

왜 이런 일이 발생하는지 이해할 수 없습니까? 누구든지 16 진수 코드가 처음 16 바이트 청크 이후에 다른 이유를 알고 있습니까?

면책 조항 : 저는 전문가가 아닙니다.

감사합니다.

Fox.txt이

The quick brown fox jumped over the lazy dog 

여기에 foxy.exe

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

만들려면 다음은 OpenSSL 명령을 실행은 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

입력 파일에 줄 바꾸기 등의 추가 문자가 포함되지 않았는지 확인하십시오. 암호화하는 동안 추가 문자가 포함됩니다.