【问题标题】:AES encrypt/decrypt textAES 加密/解密文本
【发布时间】:2015-05-16 01:58:53
【问题描述】:

我有一个包含电子邮件、用户名和密码的文本,我使用 AES 算法对其进行加密。

我在加密的时候好像没问题,但是到了解密的时候,只有一部分(拳头 48bytes = 3x 16byte 块)还可以,其余的只是一些垃圾。

我曾尝试使用 unsigned char 代替字符串,但遇到了同样的问题。

我不知道问题出在我的算法还是字符集中,但算法对我来说似乎没问题。

如果字符串包含空终止字符,会导致 string.size 出现问题吗?我不知道来自 AES 的密码是否可以包含空终止字符。

代码如下:

    int  aes_set_key( aes_context *ctx, uint8 *key, int nbits ); //Prototype
    void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] ); //Prototype
    void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] ); //Prototype
    char Buffer[255];
    memset(Buffer, 0, sizeof(Buffer));
    sprintf_s(Buffer, "%s\r\n%s\r\n%s", RegisterEdits[0], RegisterEdits[1], /*PasswordHash.c_str()*/RegisterEdits[2]); // Save email, username and password to buffer
    MSGBOX(Buffer);
    aes_context ctx;
    unsigned char key[] = "0123456789ABCDEF"; // Encrypting/Decrypting key
    unsigned char outputEnc[20];
    unsigned char outputDec[20];
    string CipherData; // Hold whole encrypted text
    string Decrypted; // Hold whole decrypted text
    memset(outputEnc, 0, sizeof(outputEnc));
    memset(outputDec, 0, sizeof(outputDec));
    aes_set_key(&ctx, key, 256);

    int Step = 0;
    char Temp[18];
    do 
    {
        memset(Temp, 0, sizeof(Temp));
        _snprintf(Temp, 16, &Buffer[Step]);//Take first 16 bytes from Buffer at address 0 and copy them into Temp
        Step += strlen(Temp); // Append the Temp size to stepper

        memset(outputEnc, 0, sizeof(outputEnc));
        aes_encrypt(&ctx, reinterpret_cast<unsigned char*>(Temp), outputEnc); // encrypt 16 bytes
        CipherData += reinterpret_cast<char*>(outputEnc); //append the 16 encrypted bytes to string
    } 
    while (Step < strlen(Buffer));
    MSGBOX((LPSTR)CipherData.c_str()); //Let me see the cipher (seems to be ok)

    //Trying little different algorithm than "do while" for decrypting
    MSGBOX("Entering");
    Step = 0;
    for(int i = CipherData.size(); i >= 0; i-=16) //At the start we have cipher size
    {
        if(i < 16) // If we have less than 16 bytes in string left...*
        {
            Beep(1000, 100);
            memset(Temp, 0, sizeof(Temp));
            memcpy(Temp, &CipherData.c_str()[Step], CipherData.size()); // *...copy only the bytes that left.
            MSGBOX(Temp);
            aes_decrypt(&ctx, reinterpret_cast<unsigned char*>(Temp), outputDec);
            Decrypted += reinterpret_cast<char*>(outputDec);
            MSGBOX((LPSTR)Decrypted.c_str());
            break;
        }
        else
        {
            //if we do have more than 16 bytes left in the string...
            memset(Temp, 0, sizeof(Temp)); 
            memcpy(Temp, &CipherData.c_str()[Step], 16); // ...Copy 16 bytes again (#)
            MSGBOX(Temp);
            aes_decrypt(&ctx, reinterpret_cast<unsigned char*>(Temp), outputDec); //decrypt 16 bytes
            Decrypted += reinterpret_cast<char*>(outputDec); //append 16 decrypted bytes
            CipherData = SubstractLastn(CipherData, 16); // IMPORTANT! Remove 16 bytes from the end of the string
            Step += 16; // Append decrypted size to stepper
            MSGBOX((LPSTR)Decrypted.c_str());
            //FIX ME! - in 3rd iteration of this loop the CipherData seems to be corrupted in the part marked with (#)
        }
    }

我会感谢你们的任何帮助!

【问题讨论】:

  • 请在您的问题中包含您正在使用的加密库。仅供参考,调试器是针对此类问题制作的。
  • 我没有使用任何库,我刚刚包含了由 Christophe Devine 用 C 语言编写的 AES 算法的源代码。如果我知道问题出在哪里,我不会发布问题。我什至不认为包括图书馆会帮助任何人解决这类问题。
  • 调试器几乎肯定会有所帮助。只是为了节省一些时间,this is the algorithm implementation 您正在使用,对吗?您的问题很可能是您将 加密 数据视为字符串可行。它不是。 AES 加密的数据可以是完整 0..255 范围内的字节,第一个问题是您构建 CipherData 的方式的主要问题。我会写一个答案,但想确保我使用的是与你相同的实现。
  • 抱歉回复晚了,我一直在睡觉 :P 是的,这是我正在使用的算法...我想,重新解释的演员会正确地投射它,一切都会好起来的,我是使用相同的方法通过 TCPIP 发送二进制数据。如果你能告诉我如何正确地做,你会非常感激,因为我仍然被困在这里。

标签: c++ algorithm encryption cryptography aes


【解决方案1】:

您将加密数据视为终止字符串。 AES 加密可以并且经常会发出 0x00 八位字节,这样做会使任何旨在将空终止字节序列视为过早无价值的算法(可以这么说)。

你的问题的核心是:

CipherData += reinterpret_cast<char*>(outputEnc);

这有效地触发了std::basic_string&lt;char&gt;operator +=(const char*) 成员,它将附加您的输出编码,就好像一个终止符被击中一样。不好。

我不会尝试修改您的代码,因为这不是我首先会这样做的方式。下面是一个加密字符串的简单(我强调simple)方法。

int main()
{
    unsigned char key[] = "0123456789ABCDEF";
    aes_context ctx = {};
    aes_set_key(&ctx, key, 128);

    // some simple message to encrypt
    std::string str = "some simple message to encrypt";

    // will hold out encrypted message
    std::vector<uint8> encryptedBytes;

    // encrypt the data.
    for (auto it = str.begin(); it != str.end();)
    {
        uint8 plain[16] = {0}, enc[16] = {0};
        size_t i = 0;
        for (; it != str.end() && i < 16; ++i,++it)
            plain[i] = *it;
        aes_encrypt(&ctx, plain, enc);
        encryptedBytes.insert(encryptedBytes.end(), enc, enc+16);
    }

    // now decrypt (not sure if this api requires resetting the
    //  key schedule, but it seems it can't hurt).
    aes_set_key(&ctx, key, 128);
    std::vector<uint8> decrypted;
    for (auto it = encryptedBytes.begin(); it != encryptedBytes.end(); it = std::next(it,16))
    {
        uint8 tmp[16];
        aes_decrypt(&ctx, &(*it), tmp);
        decrypted.insert(decrypted.end(), tmp, tmp+16);
    }

    // rebuild string from data. stop at the terminator or end.
    auto last = std::find(decrypted.begin(), decrypted.end(), 0);
    std::string res(decrypted.begin(), last);

    // show all three (original, cipher, decrypted)
    std::cout << str << '\n';
    print_hex(encryptedBytes.begin(), encryptedBytes.end());
    std::cout << res << '\n';
}

这利用了一个脑死亡的十六进制转储,它会做出大量假设,因此请自行决定使用:

template<class Iter>
void print_hex(Iter beg, Iter end)
{
    std::cout << std::hex << std::setfill('0');
    unsigned int x = 0;
    while (beg != end)
    {
        std::cout << std::setw(2) << static_cast<unsigned int>(*beg) << ' ';
        if (++beg != end && ++x % 16 == 0)
            std::cout << '\n';
    }
    std::cout << '\n';
}

使用您的 AES 加密代码运行的上述输出如下:

some simple message to encrypt
82 56 5b a7 a5 b5 6a e9 e5 a4 a6 9d bb ee 14 db 
6b 1e 54 b8 9d 7f 8c 16 18 c6 33 47 1c f1 48 25 
some simple message to encrypt

最后,有几点需要注意。首先,这迫切需要一个填充方案。一个常用的方法是PKCS7 padding,它很容易实现并确保解密文本的确切数量与原始加密相匹配。

其次,您在此代码中看到的每一个16 值都应该与一个清单常量交换,该常量是正在使用的算法的块大小。幻数编程很糟糕。不要像我那样做;做你应该做的。

祝你好运。

【讨论】:

  • 伙计,你太棒了!非常感谢你!你不知道,我多么感谢这样的帮助!
  • 你能解释一件事吗?你的意思是什么值应该与清单常量交换?
  • 宏:#define AES_BLOCK_SIZE 16
  • 没有用于加强加密的填充;它在那里用数据填充块大小,告诉你填充数据有多大。例如:PKCS7 填充总是添加到最后一个块,或者如果需要(当您的数据已经是块大小的完美倍数时)添加一个额外的块。提供的链接将向您展示加密器和解密器如何使用它,必须使用相同的填充方案。是的,我的措辞选择不当。一个宏,一个const,任何可以象征性地传达块大小而不是用幻数乱扔代码的东西。
  • 换句话说,如果最后一个块尺寸过小,则填充可以拉长最后一个块(请记住,AES 要求 16 字节块作为其方案的一部分)。解密时,您需要一种方法来了解您刚刚解密的数据中有多少是真实数据与填充字节(这样您就知道要丢弃什么)。您在加密器和解密器(它们必须相同)上选择和实现的填充算法会处理这个问题。现在我们只是零填充,它会按原样工作,但标准的填充算法会更好。
猜你喜欢
  • 2012-02-24
  • 2016-09-22
  • 2014-01-18
  • 2015-01-20
  • 2014-01-05
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多