0

我有一个问题,即使用 EVP_SealInit、EVP_SealUpdate 和 EVP_SealFinal 的加密过程正在工作并且没有返回任何失败代码。

尝试通过 EVP_OpenInit 和 EVP_OpenUpdate 解密消息,但是 EVP_OpenFinal 返回 0 失败。尽管函数返回 0,但完全解密的文本存储在输出缓冲区中,从 EVP_OpenFinal 返回的总输出长度与从返回的总长度相同EVP_SealFinal。

我引用了这篇 SO 帖子:OpenSSL RSA:无法加密/解密超过 16 个字节的消息

我相信 OP 通过修复修改了他的代码,所以我无法在那里获得太多帮助。值得注意的是,无论消息长度如何,我的问题都存在。在 EVP_OpenFinal 调用期间,10、15 和 140 字符消息都失败了,但每条消息都完全存储在输出缓冲区中。

加密:

    int envelope_seal(EVP_PKEY *pub_key, uint8_t const *plaintext, 
                      int plaintext_len, uint8_t *encrypted_key, 
                      int *encrypted_key_len, uint8_t *iv,
                      uint8_t *ciphertext)
    {
        EVP_CIPHER_CTX *ctx = NULL;

        int ciphertext_len = 0;
        int len = 0;

        if (!(ctx = EVP_CIPHER_CTX_new()))
        {
            handleErrors();

            return -1;
        }

        if (!EVP_SealInit(ctx, EVP_aes_256_gcm(), &encrypted_key,
                  encrypted_key_len, iv, &pub_key, 1))
        {
            handleErrors();

            return -1;
        }


        if (!EVP_SealUpdate(ctx, ciphertext + len, &len, plaintext, plaintext_len))
        {
            handleErrors();

            return -1;
        }

        ciphertext_len += len;

        if (!EVP_SealFinal(ctx, ciphertext + ciphertext_len, &len))
        {
            handleErrors();

            return -1;
        }

        ciphertext_len += len;

        EVP_CIPHER_CTX_free(ctx);

        return ciphertext_len;
    }

解密:

    int envelope_open(EVP_PKEY *priv_key, uint8_t const *ciphertext, 
                     int ciphertext_len, uint8_t const *encrypted_key, int encrypted_key_len, uint8_t const *iv, uint8_t *plaintext)
    {
        EVP_CIPHER_CTX *ctx = NULL;

        int plaintext_len = 0;
        int len = 0;

        if (!(ctx = EVP_CIPHER_CTX_new()))
        {
            handleErrors();

            return -1;
        }

        if (!EVP_OpenInit(ctx, EVP_aes_256_gcm(), encrypted_key,
                  encrypted_key_len, iv, priv_key))
        {
            handleErrors();

            return -1;
        }

        if (!EVP_OpenUpdate(ctx, plaintext + plaintext_len, &len, ciphertext, ciphertext_len))
        {
            handleErrors();

            return -1;
        }

        plaintext_len += len;

        if (!EVP_OpenFinal(ctx, plaintext + plaintext_len, &len))
        {
            handleErrors();

            return -1;

        }

        plaintext_len += len;

        EVP_CIPHER_CTX_free(ctx);

        return plaintext_len;
    }

错误处理:

    void handleErrors(void)
    {
        ERR_print_errors_fp(stderr);
    }

如果有人能突出显示 OpenUpdate 和 OpenFinal 之间的内部差异,那么任何帮助突出我可能忽略的内容都会非常有用,这也可能会有所帮助。

谢谢!

4

1 回答 1

1

AES_GCM 和 AES_CCM 支持执行经过身份验证的数据加密和解密的能力,这是通过在加密数据上创建 MAC 标签来实现的。MAC 标签将确保数据在传输和存储过程中不会被意外更改或恶意篡改。

加密操作的输出将是密文和标签。在解密操作过程中应使用该标签,以确保密文未被篡改。

有关更多信息,请参阅https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption

在 openssl 中使用 EVP_aes_128_gcm 用于大小不是 16 的倍数的 aad是带有标签和 aad 实现的,此处遗漏了它并导致解密失败。

在调用 EVP_SealFinal() 之后,我们需要检索加密期间使用的标签。

unsigned char tag[32]={0};
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
{
        fprintf(stderr, "EVP_CTRL_GCM_GET_TAG failed\n");
        exit(1);
}

在调用 EVP_OpenFinal 之前,我们需要传递加密期间获得的相同标签

if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
{
        fprintf(stderr, "EVP_CTRL_GCM_SET_TAG failed\n");
        exit(1);
}
于 2019-05-21T14:40:35.567 回答