2012-08-08 220 views
2

我正在做一些AES CBC和openssl的工作,現在,我被困在一個問題上,我無法猜測(一如既往)什麼是錯的。AES CBC加密/解密只解密前16個字節

給定一個長度不超過16字節的消息,加密和解密過程可以正常工作,但是當消息大於16個字節時,解密只能在第16個第一個字節上進行。

當我打電話aes.exe stackoverflow stackoverflow輸出爲:

Using: 
IVector = |000102030405060708090a0b0c0d0e0f| 
Key  = |737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f| 
Encrypted = |6c65219594c0dae778f9b5e84f018db6| 

Encrypting : stackoverflow 
With Key : stackoverflow 
Becomes : ??????¤le!òö++þx¨ÁÞO?ìÂ. 

Using: 
IVector = |000102030405060708090a0b0c0d0e0f| 
Key  = |737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f| 
Decrypted = |737461636b6f766572666c6f77| 

Decrypting : ??????¤le!òö++þx¨ÁÞO?ì 
With Key : stackoverflow 
Becomes : stackoverflow 

當我打電話aes.exe stackoverflowstackoverflow stackoverflow輸出爲:

Using: 
IVector = |000102030405060708090a0b0c0d0e0f| 
Key  = |737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f| 
Encrypted = |46172e3f7fabdcfc6c8b3e65aef175cddf8164236faf706112c15f5e765e49a5| 

Encrypting : stackoverflowstackoverflow 
With Key : stackoverflow 
Becomes : ??????¤F?.?¦½_³lï>e«±u-¯üd#o»pa?-_^v^IÑ. 

Using: 
IVector = |000102030405060708090a0b0c0d0e0f| 
Key  = |737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f| 
Decrypted = |737461636b6f766572666c6f77737461257d434a1edcbc970bf5346ea2fc7bc2| 

Decrypting : ??????¤F?.?¦½_³lï>e«±u-¯üd#o»pa?-_^v^IÑ 
With Key : stackoverflow 
Becomes : stackoverflowsta%}CJ?_+ù?§4nó³{-. 

我提供用於每個加密/解密呼叫隨機IV和歸一化兩種情況下的密碼均爲32字節;我錯過了什麼?有誰知道?

的源代碼:

#include <vector> 
#include <string> 
#include <iostream> 

// Make a Key of exactly 32 bytes, truncates or adds values if it's necessary 
std::string AES_NormalizeKey(const void *const apBuffer, size_t aSize) 
{ 
    static const unsigned char key32[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; 
    const char *const Buffer = reinterpret_cast<const char *>(apBuffer); 
    std::string Result(reinterpret_cast<const char *>(key32), 32); 
    std::copy(Buffer, Buffer + ((aSize < 32)? aSize: 32), Result.begin()); 
    return Result; 
} 

// Encrypt using AES cbc 
std::string AESEncrypt(const void *const apBuffer, size_t aBufferSize, const void *const apKey, size_t aKeySize, std::string &aIVector) 
{ 
    // Create IVector. 
    unsigned char AES_IVector[16] = {0}; 
    std::srand(static_cast<int>(time(NULL))); 
    std::generate(std::begin(AES_IVector), std::end(AES_IVector), std::rand); 
    std::copy(std::begin(AES_IVector), std::end(AES_IVector), aIVector.begin()); 

    // Create key. 
    const std::string Key(AES_NormalizeKey(apKey, aKeySize)); 
    AES_KEY EncryptKey; 
    AES_set_encrypt_key(reinterpret_cast<const unsigned char *>(Key.c_str()), 256, &EncryptKey); 

    // Encrypt. 
    unsigned char AES_Encrypted[1024] = {0}; 
    AES_cbc_encrypt(static_cast<const unsigned char *>(apBuffer), AES_Encrypted, aBufferSize, &EncryptKey, AES_IVector, AES_ENCRYPT); 
    const std::string Encrypted(reinterpret_cast<const char *>(AES_Encrypted), ((aBufferSize/16) + 1) * 16); 

    // Finish. 
    return Encrypted; 
}; 

// Decrypt using AES cbc 
std::string AESDecrypt(const void *const apBuffer, size_t aBufferSize, const void *const apKey, size_t aKeySize, std::string &aIVector) 
{ 
    // Read IVector. 
    unsigned char AES_IVector[16] = {0}; 
    std::copy(aIVector.begin(), aIVector.end(), std::begin(AES_IVector)); 

    // Create Key. 
    const std::string Key(AES_NormalizeKey(apKey, aKeySize)); 
    AES_KEY DecryptKey; 
    AES_set_decrypt_key(reinterpret_cast<const unsigned char *>(Key.c_str()), 256, &DecryptKey); 

    // Decrypt. 
    unsigned char AES_Decrypted[1024] = {0}; 
    AES_cbc_encrypt(static_cast<const unsigned char *>(apBuffer), AES_Decrypted, aBufferSize, &DecryptKey, AES_IVector, AES_DECRYPT); 
    const std::string Decrypted(reinterpret_cast<const char *>(AES_Decrypted)); 

    // Finish. 
    return Decrypted; 
}; 

// Entry point 
int main(unsigned int argc, char **argv) 
{ 
    typedef std::vector<const std::string> vs; 
    vs a; 

    for (vs::size_type Index = 0; Index < argc; ++Index) 
    { 
     a.push_back(argv[Index]); 
    } 

    if (a.size() == 3) 
    { 
     std::string IV(""); 

     std::string e(AESEncrypt(a.at(1).c_str(), a.at(1).size(), a.at(2).c_str(), a.at(2).size()), IV); 
      std::cout << "Encrypting : " << a.at(1) << "\n" 
         << "With Key : " << a.at(2) << "\n" 
         << "Becomes : " << e << ".\n"; 

     std::string d(AESDecrypt(e.c_str(), e.size(), a.at(2).c_str(), a.at(2).size()), IV); 
      std::cout << "Decrypting : " << e << "\n" 
         << "With Key : " << a.at(2) << "\n" 
         << "Becomes : " << d << ".\n"; 
    } 

    return 0; 
} 
+0

你應該*不*使用'AES_encrypt'和朋友。您應該使用'EVP_ *'功能。請參閱OpenSSL wiki上的[EVP Symmetric Encryption and Decryption](https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption)。事實上,您應該使用經過身份驗證的加密,因爲它提供了*機密性和真實性。請參閱OpenSSL wiki上的[EVP Authenticated Encryption and Decryption](https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption)。 – jww 2015-05-15 20:45:40

回答

4

你的代碼幾乎正確的,所不同的是初始化向量被覆蓋由於內存破壞,密文的長度不正確地舍入,和std ::字符串::數據( )當使用std :: string作爲字節數組時,應該使用它來代替std :: string :: c_str()。初始化向量被複制到覆蓋堆棧的空字符串中。然後初始化向量被覆蓋,AESDecrypt使用不同的值。我已經包含了結合個人建議並修復這些問題的源代碼。當

aes "Hello World!" stackoverflow
運行它產生以下輸出:

 
(Normalized key: 737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f) 
Encrypting : Hello World! 
With Key : stackoverflow 
Init Vec : d8b1657d9e2317c93430994f59bb54eb 
Becomes : ��Йw�H���}�;E 
(Normalized key: 737461636b6f766572666c6f770d0e0f101112131415161718191a1b1c1d1e1f) 
Decrypting : ��Йw�H���}�;E 
With Key : stackoverflow 
Init Vec : d8b1657d9e2317c93430994f59bb54eb 
Becomes : Hello World! 
#include <vector> 
#include <string> 
#include <iostream> 
#include <iomanip> 
#include <algorithm> 
#include <initializer_list> 
#include <openssl/aes.h> 

typedef unsigned char byte; 

template <size_t multiple> size_t round_up(const size_t len) 
{ 
    if (len % multiple == 0) return len; 
    else return ((len/multiple) + 1) * multiple; 
} 

std::ostream &print_buffer_as_hex(std::ostream &o, const unsigned char *buf, size_t size) 
{ 
    o << std::hex << std::setfill('0'); 
    for(size_t i = 0; i < size; ++i) 
    { 
     o << std::setw(2) << static_cast<unsigned int>(buf[i]); 
    } 
    return o << std::dec; 
} 

inline std::ostream &operator<<(std::ostream &o, const std::vector<byte> &buf) 
{ 
    return print_buffer_as_hex(o, reinterpret_cast<const unsigned char*>(&buf[0]), buf.size()); 
} 

// Make a Key of exactly 32 bytes, truncates or adds values if it's necessary 
std::string AES_NormalizeKey(const void *const apBuffer, size_t aSize) 
{ 
    static const unsigned char key32[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; 
    const char *const Buffer = reinterpret_cast<const char *>(apBuffer); 
    std::string Result(reinterpret_cast<const char *>(key32), 32); 
    std::copy(Buffer, Buffer + ((aSize < 32)? aSize: 32), Result.begin()); 
    return Result; 
} 

// Encrypt using AES cbc 
std::string AESEncrypt(const void *const apBuffer, size_t aBufferSize, const void *const apKey, size_t aKeySize, std::vector<byte> &aIVector) 
{ 
    // Create IVector. 
    unsigned char AES_IVector[AES_BLOCK_SIZE] = {0}; 
    std::srand(static_cast<int>(time(NULL))); 
    std::generate(std::begin(AES_IVector), std::end(AES_IVector), std::rand); 
    aIVector.resize(sizeof(AES_IVector)); 
    std::copy(std::begin(AES_IVector), std::end(AES_IVector), aIVector.begin()); 

    // Create key. 
    const std::string Key(AES_NormalizeKey(apKey, aKeySize)); 
    std::cout << "(Normalized key: "; 
    print_buffer_as_hex(std::cout, (const unsigned char*)Key.data(), Key.size()) << ")\n"; 
    AES_KEY EncryptKey; 
    AES_set_encrypt_key(reinterpret_cast<const unsigned char *>(Key.data()), 256, &EncryptKey); 

    // Encrypt. 
    unsigned char AES_Encrypted[1024] = {0}; 
    AES_cbc_encrypt(static_cast<const unsigned char *>(apBuffer), AES_Encrypted, aBufferSize, &EncryptKey, AES_IVector, AES_ENCRYPT); 
    const std::string Encrypted(reinterpret_cast<const char *>(AES_Encrypted), round_up<AES_BLOCK_SIZE>(aBufferSize)); 

    // Finish. 
    return Encrypted; 
}; 

// Decrypt using AES cbc 
std::string AESDecrypt(const void *const apBuffer, size_t aBufferSize, const void *const apKey, size_t aKeySize, std::vector<byte> &aIVector) 
{ 
    // Read IVector. 
    unsigned char AES_IVector[AES_BLOCK_SIZE] = {0}; 
    std::copy(aIVector.begin(), aIVector.end(), std::begin(AES_IVector)); 

    // Create Key. 
    const std::string Key(AES_NormalizeKey(apKey, aKeySize)); 
    std::cout << "(Normalized key: "; 
    print_buffer_as_hex(std::cout, (const unsigned char*)Key.data(), Key.size()) << ")\n"; 
    AES_KEY DecryptKey; 
    AES_set_decrypt_key(reinterpret_cast<const unsigned char *>(Key.data()), 256, &DecryptKey); 

    // Decrypt. 
    unsigned char AES_Decrypted[1024] = {0}; 
    AES_cbc_encrypt(static_cast<const unsigned char *>(apBuffer), AES_Decrypted, aBufferSize, &DecryptKey, AES_IVector, AES_DECRYPT); 
    const std::string Decrypted(reinterpret_cast<const char *>(AES_Decrypted)); 

    // Finish. 
    return Decrypted; 
}; 

// Entry point 
int main(int argc, char **argv) 
{ 
    typedef std::vector<std::string> vs; 
    vs a; 

    for (vs::size_type Index = 0; Index < static_cast<unsigned>(argc); ++Index) 
    { 
     a.push_back(argv[Index]); 
    } 

    if (a.size() == 3) 
    { 
     std::vector<byte> IV; 

     std::string e(AESEncrypt(a.at(1).data(), a.at(1).size(), a.at(2).data(), a.at(2).size(), IV)); 
      std::cout << "Encrypting : " << a.at(1) << "\n" 
         << "With Key : " << a.at(2) << "\n" 
         << "Init Vec : " << IV << "\n" 
         << "Becomes : " << e << "\n"; 

     std::string d(AESDecrypt(e.data(), e.size(), a.at(2).data(), a.at(2).size(), IV)); 
      std::cout << "Decrypting : " << e << "\n" 
         << "With Key : " << a.at(2) << "\n" 
         << "Init Vec : " << IV << "\n" 
         << "Becomes : " << d << "\n"; 
    } 
    std::cout.flush(); 

    return 0; 
} 
+0

太棒了!我測試過了,現在工作正常!至於前幾天我已經修正了它(但是因爲話題看起來死了,我沒有提到它),但使用'data'而不是'c_str'是一個非常好的點! – 2012-08-20 06:17:25

+1

我很高興能幫到你!一個小小的說明:在這種情況下寫入空字符串是否覆蓋堆棧取決於實現。大多數std :: string實現使用「小字符串優化」(即在對象中存儲小字符串),所以我懷疑這是發生在這裏的事情。 – apokluda 2012-08-21 11:52:21

0

我沒有一個具體的答案,但在這裏,將不適合在評論小費。加密和解密需要使用相同的密鑰和IV才能工作。加密函數的輸出必須進入解密函數的輸入。

因此,要調試您的問題,您需要將輸入打印到加密功能並打印其輸出。然後您需要將輸入數據打印到解密函數並打印其輸出。純文本是一個不好的方法來做到這一點,因爲你不能看到真正的字節。因此,按十六進制值輸出密鑰,IV和數據。

#include <iostream> 
#include <iomanip> 
... 
std::ostream &print_buffer_as_hex(std::ostream &o, const unsigned char *buf, size_t size) 
{ 
    for(int i = 0; i < size; ++i) 
    { 
     o << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(buf[i]) << std::dec; 
    } 
    o << "\n"; 

    return o; 
} 

調用它像這樣:

print_buffer_as_hex(std::cout, reinterpret_cast<const char *>(AES_Encrypted), ((aBufferSize/16) + 1) * 16); 

我會使用std::vector<unsigned char>而不是std::string持有任意字節。使用構造函數或方法(resize(),而不是reserve()!)設置所需的空間量。如果您調用需要unsigned char *的API函數,只需傳遞&vec[0],其中vec是您的矢量對象。你的代碼看起來會更乾淨。

例如,

std::vector<unsigned char> iv(16); 
std::srand(static_cast<int>(time(NULL))); 
std::generate(iv.begin(), iv.end(), std::rand); 

print_buffer_as_hex(std::cout, &iv[0], iv.size()); 
+0

構建'std :: string Encrypted'不是問題,我知道'std :: string'構造函數的n參數,'((aBufferSize/16)+ 1)* 16)'有故意,導致AES加密總是創建多個16bytes數據的塊,所以如果消息是12bytes,則加密是16,如果消息是17bytes數據,則加密是32個字節。我將把中間字節放在hexa上,並作爲結果。 – 2012-08-08 18:29:34

+0

@PaperBirdMaster:我今天學到了一些新東西。我完全錯誤了'std :: string'構造函數如何處理二進制數據和'n'參數。感謝您指出,我已經編輯了相應的答案。 – indiv 2012-08-08 18:47:18