OpenSSL:AES CCM对块大文件的256位加密:是否可能?

时间:2013-02-28 14:22:25

标签: c++ c openssl aes encryption-symmetric

我正在处理使用 AES CCM模式 256位密钥长度)加密大型文件的任务。其他加密参数是:

  • 标记大小:8字节
  • iv 大小:12字节

由于我们已经使用 OpenSSL 1.0.1c ,我也希望将它用于此任务。

事先不知道文件的大小,它们可能非常大。这就是为什么我想通过块读取它们并使用 EVP_EncryptUpdate 单独加密每个块直到文件大小。

不幸的是,只有当整个文件一次加密时,加密才能为我。如果我试图多次调用它,我会从EVP_EncryptUpdate或奇怪的崩溃中得到错误。我使用gcc 4.7.2在Windows 7和Ubuntu Linux上测试了加密。

我无法在OpenSSL站点上找到并且无法(或可能)逐块加密数据的信息。

其他参考资料:

请参阅下面的代码,演示我尝试实现的目标。不幸的是,它在for循环中指示失败。

#include <QByteArray>
#include <openssl/evp.h>

// Key in HEX representation
static const char keyHex[] = "d896d105b05aaec8305d5442166d5232e672f8d5c6dfef6f5bf67f056c4cf420";
static const char ivHex[]  = "71d90ebb12037f90062d4fdb";

// Test patterns
static const char orig1[] = "Very secret message.";

const int c_tagBytes      = 8;
const int c_keyBytes      = 256 / 8;
const int c_ivBytes       = 12;

bool Encrypt()
{
    EVP_CIPHER_CTX *ctx;
    ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    QByteArray keyArr = QByteArray::fromHex(keyHex);
    QByteArray ivArr = QByteArray::fromHex(ivHex);

    auto key = reinterpret_cast<const unsigned char*>(keyArr.constData());
    auto iv = reinterpret_cast<const unsigned char*>(ivArr.constData());

    // Initialize the context with the alg only
    bool success = EVP_EncryptInit(ctx, EVP_aes_256_ccm(), nullptr, nullptr);
    if (!success) {
        printf("EVP_EncryptInit failed.\n");
        return success;
    }

    success = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, c_ivBytes, nullptr);
    if (!success) {
        printf("EVP_CIPHER_CTX_ctrl(EVP_CTRL_CCM_SET_IVLEN) failed.\n");
        return success;
    }
    success = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, c_tagBytes, nullptr);
    if (!success) {
        printf("EVP_CIPHER_CTX_ctrl(EVP_CTRL_CCM_SET_TAG) failed.\n");
        return success;
    }

    success = EVP_EncryptInit(ctx, nullptr, key, iv);
    if (!success) {
        printf("EVP_EncryptInit failed.\n");
        return success;
    }

    const int bsize = 16;
    const int loops = 5;
    const int finsize = sizeof(orig1)-1; // Don't encrypt '\0'

    // Tell the alg we will encrypt size bytes
    // http://www.fredriks.se/?p=23
    int outl = 0;
    success = EVP_EncryptUpdate(ctx, nullptr, &outl, nullptr, loops*bsize + finsize);
    if (!success) {
        printf("EVP_EncryptUpdate for size failed.\n");
        return success;
    }
    printf("Set input size. outl: %d\n", outl);

    // Additional authentication data (AAD) is not used, but 0 must still be
    // passed to the function call:
    // http://incog-izick.blogspot.in/2011/08/using-openssl-aes-gcm.html
    static const unsigned char aadDummy[] = "dummyaad";
    success = EVP_EncryptUpdate(ctx, nullptr, &outl, aadDummy, 0);
    if (!success) {
        printf("EVP_EncryptUpdate for AAD failed.\n");
        return success;
    }
    printf("Set dummy AAD. outl: %d\n", outl);

    const unsigned char *in = reinterpret_cast<const unsigned char*>(orig1);
    unsigned char out[1000];
    int len;

    // Simulate multiple input data blocks (for example reading from file)
    for (int i = 0; i < loops; ++i) {
        // ** This function fails ***
        if (!EVP_EncryptUpdate(ctx, out+outl, &len, in, bsize)) {
            printf("DHAesDevice: EVP_EncryptUpdate failed.\n");
            return false;
        }
        outl += len;
    }

    if (!EVP_EncryptUpdate(ctx, out+outl, &len, in, finsize)) {
        printf("DHAesDevice: EVP_EncryptUpdate failed.\n");
        return false;
    }
    outl += len;

    int finlen;
    // Finish with encryption
    if (!EVP_EncryptFinal(ctx, out + outl, &finlen)) {
        printf("DHAesDevice: EVP_EncryptFinal failed.\n");
        return false;
    }
    outl += finlen;
    // Append the tag to the end of the encrypted output
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, c_tagBytes, out + outl)) {
        printf("DHAesDevice: EVP_CIPHER_CTX_ctrl failed.\n");
        return false;
    };
    outl += c_tagBytes;
    out[outl] = '\0';

    EVP_CIPHER_CTX_cleanup(ctx);
    EVP_CIPHER_CTX_free(ctx);

    QByteArray enc(reinterpret_cast<const char*>(out));

    printf("Plain text size: %d\n", loops*bsize + finsize);
    printf("Encrypted data size: %d\n", outl);

    printf("Encrypted data: %s\n", enc.toBase64().data());

    return true;
}

编辑(错误的解决方案)

我收到的反馈让我想到了另一个方向,我发现必须为每个块调用EVP_EncryptUpdate进行加密,不是因为它的总大小文件。我在块加密之前移动它:像这样:

for (int i = 0; i < loops; ++i) {
    int buflen;
    (void)EVP_EncryptUpdate(m_ctx, nullptr, &buflen, nullptr, bsize);
    // Resize the output buffer to buflen here
    // ...
    // Encrypt into target buffer
    (void)EVP_EncryptUpdate(m_ctx, out, &len, in, buflen);
    outl += len;
}

AES CCM加密逐块加载,但不正确,因为每个块都被视为独立消息。

编辑2

只有在完整邮件一次加密的情况下,OpenSSL的实现才能正常工作。

http://marc.info/?t=136256200100001&r=1&w=1

我决定改用Crypto ++。

2 个答案:

答案 0 :(得分:0)

对于AEAD-CCM模式,您无法在关联数据馈送到上下文后加密数据。 加密所有数据,并且只有在传递相关数据后才会加密。

答案 1 :(得分:0)

我在这里发现了一些误解

首先       EVP_EncryptUpdate(ctx,nullptr,&amp; outl       调用这种方式是知道需要多少输出缓冲区,这样你就可以分配缓冲区,第二次将第二个参数作为有效足够大的缓冲区来保存数据。

当您实际添加加密输出时,您也传递错误(通过之前的调用写入)。