2012-08-08 126 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