You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenSSL 3中ChaCha20-Poly1305不验证标签问题排查及用法咨询

OpenSSL ChaCha20-Poly1305 加密/解密与标签验证问题排查及正确用法

问题描述

使用OpenSSL的EVP_chacha20_poly1305()进行加密和MAC操作时遇到异常:

  1. 加密完成后,认证标签未自动附加到密文末尾
  2. 解密时返回成功状态码1,但未执行标签验证逻辑,也无法获取或验证标签

问题代码:

int main( )
{
    unsigned char key[] = "01234567890123456789012345678912";
    unsigned char iv[]  = "0123456789012345";

    unsigned char pt[23] = "plaintext of length 23";
    unsigned char ct[23 + 16];
    unsigned char dt[23];

    memset( ct, '\0', sizeof( ct ) );
    memset( dt, '\0', sizeof( dt ) );

    int ct_bw{ 0 };
    int dt_bw{ 0 };
    int status{ -19 };
    {
        // Encrypt
        EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new();
        EVP_EncryptInit_ex2( ectx, EVP_chacha20_poly1305(), key, iv,
                                         nullptr );

        EVP_EncryptUpdate( ectx, ct, &ct_bw, pt, 23 );

        int bw = ct_bw;

        EVP_EncryptFinal_ex( ectx, ct + ct_bw, &ct_bw );
        ct_bw += bw;

        EVP_CIPHER_CTX_free( ectx );
        for(int i=24;i<34;i++){
            //tag id not stored at the end of the ciphertext!!!
            ct[i]='\0';
        }

    }
    {
        // Decrypt
        EVP_CIPHER_CTX* dctx = EVP_CIPHER_CTX_new();
        EVP_DecryptInit_ex( dctx, EVP_chacha20_poly1305(), nullptr,
                                        key, iv );

        EVP_DecryptUpdate( dctx, dt, &dt_bw, ct, ct_bw );

        status = EVP_DecryptFinal_ex( dctx, dt + dt_bw, &dt_bw );

        //decrypts sucsessfully, with sucsess status code of 1
        //does not verify tag, or write it anywhere
        printf("\nstatus:%i decrypted_test:%s\n",status,dt);

        EVP_CIPHER_CTX_free( dctx );
    }

}

补充:EVP_chacha20与EVP_chacha20_poly1305()不具备互操作性(本就不应互操作)。


错误点拆解

  1. 加密阶段未主动获取认证标签
    ChaCha20-Poly1305的认证标签不会自动附加到密文后,必须手动调用EVP_CIPHER_CTX_ctrl()配合EVP_CTRL_AEAD_GET_TAG参数获取标签,当前代码跳过了这一步,导致无法拿到标签。

  2. 解密阶段未传入待验证标签
    解密时必须明确传入加密生成的标签,OpenSSL才会执行标签验证逻辑。当前解密流程未提供标签,EVP_DecryptFinal_ex()仅完成解密,未做认证校验,因此返回成功。

  3. 解密初始化参数顺序不规范
    EVP_DecryptInit_ex()的正确参数顺序应为ctx, cipher, nullptr, key, iv,当前代码将key和nullptr位置写反,虽OpenSSL可能兼容,但需修正为规范写法。


正确实现代码

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>

int main()
{
    // 实际场景中密钥和IV需用安全随机数生成,此处仅作示例
    unsigned char key[32] = "01234567890123456789012345678912";
    unsigned char iv[12] = "012345678901"; // ChaCha20-Poly1305标准IV长度为12字节

    unsigned char pt[] = "plaintext of length 23";
    size_t pt_len = strlen((char*)pt);
    // 密文缓冲区:明文长度 + 标签长度(16字节)
    unsigned char ct[pt_len + 16];
    unsigned char dt[pt_len];
    unsigned char tag[16]; // 存储认证标签

    memset(ct, 0, sizeof(ct));
    memset(dt, 0, sizeof(dt));
    memset(tag, 0, sizeof(tag));

    int ct_bw = 0;
    int dt_bw = 0;
    int status = -1;

    // -------------------------- 加密流程 --------------------------
    {
        EVP_CIPHER_CTX* ectx = EVP_CIPHER_CTX_new();
        if (!ectx) {
            printf("Failed to create encrypt context\n");
            return -1;
        }

        if (EVP_EncryptInit_ex2(ectx, EVP_chacha20_poly1305(), key, iv, nullptr) != 1) {
            printf("EncryptInit failed\n");
            EVP_CIPHER_CTX_free(ectx);
            return -1;
        }

        if (EVP_EncryptUpdate(ectx, ct, &ct_bw, pt, pt_len) != 1) {
            printf("EncryptUpdate failed\n");
            EVP_CIPHER_CTX_free(ectx);
            return -1;
        }

        int final_bw = 0;
        if (EVP_EncryptFinal_ex(ectx, ct + ct_bw, &final_bw) != 1) {
            printf("EncryptFinal failed\n");
            EVP_CIPHER_CTX_free(ectx);
            return -1;
        }
        ct_bw += final_bw; // 此时ct_bw等于明文长度

        // 获取认证标签
        if (EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_GET_TAG, 16, tag) != 1) {
            printf("Failed to get tag\n");
            EVP_CIPHER_CTX_free(ectx);
            return -1;
        }

        // 将标签附加到密文末尾(也可单独存储)
        memcpy(ct + ct_bw, tag, 16);
        ct_bw += 16;

        EVP_CIPHER_CTX_free(ectx);
        printf("Encrypted successfully, ciphertext + tag length: %d\n", ct_bw);
    }

    // -------------------------- 解密流程 --------------------------
    {
        EVP_CIPHER_CTX* dctx = EVP_CIPHER_CTX_new();
        if (!dctx) {
            printf("Failed to create decrypt context\n");
            return -1;
        }

        if (EVP_DecryptInit_ex2(dctx, EVP_chacha20_poly1305(), key, iv, nullptr) != 1) {
            printf("DecryptInit failed\n");
            EVP_CIPHER_CTX_free(dctx);
            return -1;
        }

        // 解密密文部分(去掉末尾16字节的标签)
        int ciphertext_len = ct_bw - 16;
        if (EVP_DecryptUpdate(dctx, dt, &dt_bw, ct, ciphertext_len) != 1) {
            printf("DecryptUpdate failed\n");
            EVP_CIPHER_CTX_free(dctx);
            return -1;
        }

        // 设置待验证的标签
        if (EVP_CIPHER_CTX_ctrl(dctx, EVP_CTRL_AEAD_SET_TAG, 16, ct + ciphertext_len) != 1) {
            printf("Failed to set tag for verification\n");
            EVP_CIPHER_CTX_free(dctx);
            return -1;
        }

        // 完成解密并验证标签
        int final_bw = 0;
        status = EVP_DecryptFinal_ex(dctx, dt + dt_bw, &final_bw);
        dt_bw += final_bw;

        if (status == 1) {
            printf("Decrypt and tag verification successful!\n");
            printf("Decrypted text: %s\n", dt);
        } else {
            printf("Tag verification failed! Status code: %d\n", status);
        }

        EVP_CIPHER_CTX_free(dctx);
    }

    return 0;
}

关键注意事项

  • IV长度规范:ChaCha20-Poly1305标准IV长度为12字节(96位),遵循规范可避免兼容性问题。
  • 标签处理逻辑:
    • 加密后必须主动调用EVP_CTRL_AEAD_GET_TAG获取标签,可选择附加到密文末尾或单独存储。
    • 解密前必须调用EVP_CTRL_AEAD_SET_TAG传入标签,否则不会执行标签验证。
  • 上下文安全:每个加密/解密流程需创建独立的EVP_CIPHER_CTX,使用完毕后调用EVP_CIPHER_CTX_free()释放资源。
  • 错误检查:所有OpenSSL API返回值都要检查,返回1表示成功,非1表示失败,避免忽略潜在问题。

内容的提问来源于stack exchange,提问作者xzijoq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 19:31:06