AES加密/解密文本

时间:2015-05-16 01:58:54

标签: c++ algorithm encryption cryptography aes

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

当我加密时似乎没问题,但是当它进行解密时,只有一部分(第48个字节= 3个16字节块)可以,但其余的只是一些垃圾。

我曾尝试使用unsigned char而不是string,但它有同样的问题。

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

如果字符串包含空终止字符,会导致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 (#)
        }
    }

我会恭喜你们的任何帮助!

1 个答案:

答案 0 :(得分:1)

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

问题的核心是:

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

这会有效地触发operator +=(const char*)的{​​{1}}成员,该成员将附加您的输出编码,就好像终结符已被命中一样。不好。

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

std::basic_string<char>

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

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';
}

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

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';
}

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

其次,您在此代码中看到的每个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 值都应与显式常量交换,该常量是所用算法的块大小。幻数编程很糟糕。不要像我一样做;尽你所能。

祝你好运。