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

OpenSSL中EC公钥验签时释放EC_KEY引发崩溃问题求助

EC验签函数EC_KEY_free崩溃问题排查

我拥有64字节字符串格式的EC公钥x、y坐标,需使用已有签名验证指定消息。编写了如下验签函数,但执行EC_KEY_free(ec_key)行时发生崩溃,注释该行则无崩溃现象。恳请提供问题原因提示或建议。

示例公钥x、y值

x is 5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE 
y is C05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1 

转换为BIGNUM后的打印值

BIGNUM x coordinate:  0x5CA6D40011278E3FF84FD94DD80E370CD90F2A304E9CEFB946082AB82FF0DBDE
BIGNUM y coordinate:  0xC05CA8BC76B1BF49C085672B76D19368394F2EDEF4A11D492BE34F14F3803AD1

验签函数代码

int verifySignature(const unsigned char* message, size_t message_len, const unsigned char* signature, size_t signature_len, const char* public_key_x, const char* public_key_y) {
    int result = 0;
    EC_KEY* ec_key = NULL;
    EC_POINT* ec_point = NULL;
    EVP_PKEY* public_key = NULL;
    EVP_MD_CTX* md_ctx = NULL;

    // parse public key
    BIGNUM* x = BN_new();
    BIGNUM* y = BN_new();

    if (!BN_hex2bn(&x, public_key_x)) {
        perror("Error parsing public key x coordinate");
        return -1;
    }

    printBN("BIGNUM x coordinate: ", x);

    if (!BN_hex2bn(&y, public_key_y)) {
        perror("Error parsing public key y coordinate");
        return -1;
    }

    printBN("BIGNUM y coordinate: ", y);

    /*
      An EC_KEY represents a public key and (optionally) an associated private key.
      A new EC_KEY (with no associated curve) can be constructed by calling EC_KEY_new. 
      The reference count for the newly created EC_KEY is initially set to 1.
      A curve can be associated with the EC_KEY by calling EC_KEY_set_group.

      Alternatively a new EC_KEY can be constructed by calling EC_KEY_new_by_curve_name
      and supplying the nid of the associated curve. Run "openssl ecparam -list_curves" for curve names. 
      This function simply wraps calls to EC_KEY_new and EC_GROUP_new_by_curve_name.
    */
    // Create the EC key with the curve SECP256R1.
    if ((ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
        fprintf(stderr, "Error creating EC key\n");
        goto end;
    }

    // Create the EC point. EC_KEY_get0_group returns the EC_GROUP associated with the EC_KEY.
    if ((ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key))) == NULL) {
        fprintf(stderr, "Error creating EC point\n");
        goto end;
    }

    // Set the x and y coordinates of the point
    if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), ec_point, x, y, NULL)) {
        fprintf(stderr, "Error setting EC point coordinates\n");
        goto end;
    }

    // Set the public key for the EC key
    if (!EC_KEY_set_public_key(ec_key, ec_point)) {
        fprintf(stderr, "Error setting EC public key\n");
        goto end;
    }

    // Assign the EC key to the EVP_PKEY
    if ((public_key = EVP_PKEY_new()) == NULL) {
        fprintf(stderr, "Error creating EVP_PKEY\n");
        goto end;
    }

    if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
        fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
        goto end;
    }

    // Create the message digest context
    if ((md_ctx = EVP_MD_CTX_new()) == NULL) {
        fprintf(stderr, "Error creating message digest context\n");
        goto end;
    }

    // Initialize the message digest context with SHA-256 and the public key
    if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key)) {
        fprintf(stderr, "Error initializing message digest context\n");
        goto end;
    }

    // Update the message digest context with the message
    if (!EVP_DigestVerifyUpdate(md_ctx, message, message_len)) {
        fprintf(stderr, "Error updating message digest context\n");
        goto end;
    }

    // Verify the signature
    result = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
    printf("Result: %d\n", result);

end:
    if (result != 1) {
        ERR_print_errors_fp(stderr);
    }

    if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
    if (public_key != NULL) EVP_PKEY_free(public_key);
    if (ec_point != NULL) EC_POINT_free(ec_point);
    
    // FIXME: here it crashes
    if (ec_key != NULL) EC_KEY_free(ec_key); 

    return result;
}

问题原因与修复方案

核心原因

EVP_PKEY_assign_EC_KEY函数会接管ec_key的所有权,当调用EVP_PKEY_free(public_key)时,OpenSSL会自动释放关联的ec_key对象。此时再执行EC_KEY_free(ec_key)属于重复释放内存,直接触发崩溃。

修复方式(二选一)

  1. 删除EC_KEY_free(ec_key)行:既然EVP_PKEY_free已经负责释放ec_key,无需手动重复释放。
  2. 赋值后清空ec_key指针:在调用EVP_PKEY_assign_EC_KEY后,立即将ec_key置为NULL,这样后续的EC_KEY_free判断会跳过:
    if (!EVP_PKEY_assign_EC_KEY(public_key, ec_key)) {
        fprintf(stderr, "Error assigning EC key to EVP_PKEY\n");
        goto end;
    }
    ec_key = NULL; // 新增这一行
    

额外内存泄漏修复

代码中创建的BIGNUM* x和BIGNUM* y没有释放,会导致内存泄漏,需要在end块中添加释放逻辑:

end:
    if (result != 1) {
        ERR_print_errors_fp(stderr);
    }

    // 新增:释放BIGNUM对象
    if (x != NULL) BN_free(x);
    if (y != NULL) BN_free(y);

    if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx);
    if (public_key != NULL) EVP_PKEY_free(public_key);
    if (ec_point != NULL) EC_POINT_free(ec_point);
    
    // 已修复:要么删除该行,要么确保ec_key为NULL
    if (ec_key != NULL) EC_KEY_free(ec_key); 

    return result;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 03:07:03