将IV附加到CipherText会占用前几个字节的纯文本

时间:2013-09-12 13:14:28

标签: c encryption memcpy initialization-vector

我在解密字符串时遇到了一些麻烦。令人气愤的是它只是前几个字节搞砸了,剩下的字符是正确的。

因此,当我使用硬编码IV进行加密和解密时,我的测试程序运行正常。该程序接受一个字符串,加密它(使用AES),然后我得到加密二进制的十六进制表示。当我试图将IV附加到密文的末尾时出现问题。特别是,在附加IV之后,十六进制字符串的长度没有增加。但是,解密函数似乎是从密文结尾处获取IV,否则它将无法解密任何一个,对吧?

我尝试了很多不同的东西,比如创建一个需要的确切大小的缓冲区,并使用memcpy添加密文和IV。这是代码:

AES加密

int encryptAes(const char *plainText, char *cipherText, const char *key) {
    unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

    int plainTextLength = strlen(plainText);
    int cipherTextLength = 0;
    int blockLength = 0;
    static const int MAX_PADDING_LENGTH = 16;

    EVP_CIPHER_CTX encryptCtx;
    EVP_CIPHER_CTX_init(&encryptCtx);

    EVP_EncryptInit_ex(&encryptCtx, EVP_aes_256_cbc(), NULL, key, iv);

    if (!EVP_EncryptUpdate(&encryptCtx, cipherText, &blockLength, (unsigned char *) plainText, plainTextLength) ) {
        printf("Error in EVP_EncryptUpdate \n");
        return 1;
    }
    cipherTextLength += blockLength;

    if (!EVP_EncryptFinal_ex(&encryptCtx, cipherText + cipherTextLength, &blockLength)) {
        printf("Error in EVP_EncryptFinal_ex \n");
        return 1;
    }
    cipherTextLength += blockLength;

    // Append the IV
    memcpy(cipherText + cipherTextLength, iv, 16);

    EVP_CIPHER_CTX_cleanup(&encryptCtx);
    return cipherTextLength;
}

AES解密

int decryptAes(const char *cipherText, char *decipheredPlainText, const size_t cipherTextLength, const char *key) {
//    unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
    unsigned char iv[16];
    memcpy(iv, cipherText + cipherTextLength, 16);

    int plainTextLength = 0;
    int blockLength = 0;

    EVP_CIPHER_CTX decryptCtx;
    EVP_CIPHER_CTX_init(&decryptCtx);

    EVP_DecryptInit_ex(&decryptCtx, EVP_aes_256_cbc(), NULL, key, iv);

    if (!EVP_DecryptUpdate(&decryptCtx, decipheredPlainText, &blockLength, cipherText, cipherTextLength)) {
        printf("Error in EVP_DecryptUpdate\n");
        return 1;
    }

    plainTextLength += blockLength;

    if (!EVP_DecryptFinal_ex(&decryptCtx, decipheredPlainText + plainTextLength, &blockLength)) {
        printf("Error in EVP_DecryptFinal_ex\n");
        return 1;
    }

    plainTextLength += blockLength;
    decipheredPlainText[plainTextLength] = '\0';

    EVP_CIPHER_CTX_cleanup(&decryptCtx);
    return plainTextLength;
}

这会产生输出:

Original Plain Text     [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]
Hexadecimal is          [be1c1aaa5827be124023a96a3360da922c244acd845e8914d03cfac69d312948e10f8ef7a99a64acbc6996724315f6cb0bf441ba3b08ab25cae64389f6ded77b1579e847d3e18ca89e71a3c4ec5ca4e3089b7bc2e6bc9ef8d175406bf4b53005a91e285d117e5990176d85793bd75853]
Decrypted Plain Text    [�}kaw&d��~C�Rmfpher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]

如果我从加密函数中删除行memcpy(cipherText + cipherTextLength, iv, 16);,并在解密函数开始时取消注释硬编码IV(并注释以下两行),则输出正确:

Original Plain Text     [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]
Hexadecimal is          [be1c1aaa5827be124023a96a3360da922c244acd845e8914d03cfac69d312948e10f8ef7a99a64acbc6996724315f6cb0bf441ba3b08ab25cae64389f6ded77b1579e847d3e18ca89e71a3c4ec5ca4e3089b7bc2e6bc9ef8d175406bf4b53005a91e285d117e5990176d85793bd75853]
Decrypted Plain Text    [cipher cipher cipher cipher CIPHER TEXT! 187? 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y]

注意!,两种情况下的十六进制相同。

任何人都可以在这里看到我出错的地方。我之前遇到过类似的东西,但似乎无法记住我是如何绕过它的。显然,在尝试附加IV时会有一些事情发生。 cipherText中有足够的空间来容纳IV的添加。

提前致谢。

2 个答案:

答案 0 :(得分:2)

使用相同的密钥但不正确的IV解密文本实际上会破坏明文输出的第一个块并正确解密其余的块。所以我会说你的IV是不正确的,即使在前几个字节之后明文是正​​确的。

答案 1 :(得分:1)

encryptAes()应该表示的返回值是多少?在我看来,它应该代表加密过程产生的数据的字节长度。您是否在考虑需要处理的数据比这长16个字节的事实?如果您在一个地方加密数据,并将其复制到另一个地方以致电decryptAes(),则您可能忘记考虑额外的十六个字节。

如果是这种情况,那么你有效的做法是:

  1. 调用encryptAes(),将文本传递给加密,输出缓冲区和加密密钥。
  2. encryptAes()中,您生成IV,执行加密,将加密数据写入输出缓冲区,计算长度。
  3. 接下来,将IV复制到加密数据的末尾,而不将其添加到cipherTextLength计数器
  4. encryptAes()返回,将cipherTextLength返回给来电者。请注意,这是比实际数据短16个字节的值。
  5. 使用此返回值作为数据长度,将数据复制到您将解密它的位置。
  6. 调用decryptAes(),将文本传递给解密,输出缓冲区,加密数据的大小和密钥。
  7. decryptAes()中,将紧随加密数据的16个随机字节数据复制到IV中。
  8. 使用此随机数据作为您的IV来运行解密。
  9. 这是我能想到的最有可能的解释,为什么你的IV会被破坏。