【问题标题】:My AES encryption/decryption functions don't work with random ivecs我的 AES 加密/解密功能不适用于随机 ivecs
【发布时间】:2011-02-02 06:34:20
【问题描述】:

我很无聊,写了一个围绕 openSSL 的包装器,以减少工作量来进行 AES 加密。如果我这样做: http://pastebin.com/V1eqz4jp (ivec = 0)
一切正常,但默认的 ivec 为全 0,存在一些安全问题。由于无论如何我都将数据作为字符串传回,我想,为什么不生成一个随机 ivec 并将其粘贴在前面,当我解密字符串时将其取下?但由于某种原因,它不起作用。

实际上,它几乎可以工作。似乎解密了字符串的中间,而不是开头或end

String is: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Encrypting..
���l%%1u���B!
�����`pN)�ɶ���[l�ӏ��{�Q�?�2�/�HԵ�y"�=Z�Cu����l%%1u���B!

Decrypting..
String is: �%���G*�5J�0��0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF

老实说,我不知道出了什么问题。也许是一些愚蠢的错误,或者我错过了关于 AES 的一些东西?

这是代码:(已编辑以将 Steve Jessop 的解决方案纳入我的第一个问题)

/*!
 * Simple AES
 * Brendan Long
 * March 29, 2010
 * 
 * Simplified encryption and decryption using OpenSSL's AES library.
 * Remember to compile with -lcrypto and link against the library
 * g++ (your stuff) -lcrypto simpleAes.cpp (or simpleAes.o)
 *
 * Implementation note: Using the default ivec (0) is not secure. For
 *                      the full security that AES offers, use a different
 *                      ivec each time (it does not need to be secret,
 *                      just different.
 *
 * This code is released into the public domain. Yada yada..
 * Read this for details: http://creativecommons.org/licenses/publicdomain/
 *
 * If for some reason public domain isn't good enough, you may use, alter,
 * distribute or do anything else you want with this code with no restrictions.
 */

#include <openssl/aes.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>

bool seed = true;

/*!
 * Encrypts a string using AES with a 256 bit key
 * Note: If the key is less than 32 bytes, it will be null padded.
 *       If the key is greater than 32 bytes, it will be truncated
 * \param in The string to encrypt
 * \param key The key to encrypt with
 * \return The encrypted data
 */
std::string aes_encrypt(std::string in, std::string key){

    // Seed the random number generator once
    if(seed){
        srand( (unsigned int) time(NULL));
        seed = false;
    }

    // Generate a random ivec
    unsigned char ivec[16];
    for(int i=0; i<16; i++){
        ivec[i] = (unsigned char) rand();
    }

     // Round up to AES_BLOCK_SIZE
    size_t textLength = ((in.length() / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;

    // Always pad the key to 32 bits.. because we can
    if(key.length() < 32){
        key.append(32 - key.length(), '\0');
    }

    // Get some space ready for the output
    unsigned char *output = new unsigned char[textLength];

    // Generate a key
    AES_KEY *aesKey = new AES_KEY;
    AES_set_encrypt_key((unsigned char*)key.c_str(), 256, aesKey);

    // Encrypt the data
    AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length() + 1, aesKey, ivec, AES_ENCRYPT);

    // Make the data into a string
    std::string ret((char*) output, textLength);

    // Add the ivec to the front
    ret = std::string((char*)ivec, 16) + ret;

    // Clean up
    delete output;
    delete aesKey;

    return ret;
}

/*!
 * Decrypts a string using AES with a 256 bit key
 * Note: If the key is less than 32 bytes, it will be null padded.
 *       If the key is greater than 32 bytes, it will be truncated
 * \param in The string to decrypt
 * \param key The key to decrypt with
 * \return The decrypted data
 */
std::string aes_decrypt(std::string in, std::string key){

    // Get the ivec from the front
    unsigned char ivec[16];
    for(int i=0;i<16; i++){
        ivec[i] = in[i];
    }

    in = in.substr(16);

    // Always pad the key to 32 bits.. because we can
    if(key.length() < 32){
        key.append(32 - key.length(), '\0');
    }

    // Create some space for output
    unsigned char *output = new unsigned char[in.length()]; 

    // Generate a key
    AES_KEY *aesKey = new AES_KEY;
    AES_set_decrypt_key((unsigned char*)key.c_str(), 256, aesKey); // key length is in bits, so 32 * 8 = 256

    // Decrypt the data
    AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length(), aesKey, ivec, AES_DECRYPT);

    // Make the output into a string
    std::string ret((char*) output);

    // Clean up
    delete output;
    delete aesKey;

    return ret;
}

【问题讨论】:

标签: c++ openssl aes


【解决方案1】:

您应该在加密之前将 ivec[16] 保存到“输出”中。 就是这样……

我还想补充一点,使用 char* 而不是字符串会更简单。

【讨论】:

    【解决方案2】:

    这一行是错误的:

    std::string ret((char*) output);
    

    解密后的数据没有 nul 终止符,因为您加密了 in.length() 字节。这说明了最后的垃圾,而不是开始的垃圾。可能还有其他问题。

    【讨论】:

      【解决方案3】:

      我的一个朋友发现了这个问题。我正在这样做:

      1. 生成随机数并存储在ivec
      2. ivec加密数据
      3. 将 ivec 附加到输出数据的开头

      问题是第2步改变了ivec的内容。我基本上是在字符串的开头存储随机数。解决方案是添加这个:

      unsigned char ivec[16];
      // set ivec to random numbers
      std::string ivecString((char*) ivec, 16);
      // encrypt data
      return ivecString + encryptedData;
      

      【讨论】:

        【解决方案4】:

        通常,您不能将加密阶段的输出视为字符串,除非您执行额外的步骤,例如对输出进行 Base 64 编码。任何输出字节都可以是 nul。

        【讨论】:

        • 是的,但我假设我的 encrypt() 函数用于加密数据,而我的 encrypt() 函数始终使用以空字符结尾的字符串。
        • 您的加密函数使用以空字符结尾的字符串 - 但加密过程中的 any 输出字节可能为空。 ivec 缓冲区的任何字节都可以为空。您需要停止将这些字节数组视为可隐式转换为合理的字符串值,除非您对它们进行适当编码
        • 一个 std::string 可以毫无问题地包含 nul 字节。这就是 (char*, size_t) 构造函数的用途,提问者的代码(主要)使用它。
        猜你喜欢
        • 1970-01-01
        • 2020-03-20
        • 2020-04-28
        • 1970-01-01
        • 2015-05-01
        • 2016-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多