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

使用mbedtls无法匹配证书/私钥公钥到mbedtls_ecdh_context问题排查

ECDH公钥匹配问题:mbedtls v3.3.0中ctx_bob.Q与ctx_pub_bob.Q不匹配的错误分析

基于MBEDTLS_ECP_DP_SECP521R1曲线生成证书和私钥,使用mbedtls v3.3.0实现提取公钥并存入mbedtls_ecdh_context结构体,但打印出的ctx_bob.Q和ctx_pub_bob.Q公钥未匹配,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "mbedtls/platform.h"
#include "mbedtls/error.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/pk.h"
#include "mbedtls/x509_crt.h"

static void hexdump(void const *bptr, size_t bytes)
{
    unsigned char *origin = (unsigned char *)(bptr);
    unsigned block = 0x10;
    size_t offset = 0;
    size_t lower = block * (offset / block);
    size_t upper = block + lower;
    size_t index = 0;
    char buffer[ADDRSIZE + 72];
    char *output;

    while (lower < bytes)
    {
        output = buffer + ADDRSIZE;
        for (index = lower; output-- > buffer; index >>= 4)
        {
            *output = DIGITS_HEX[index & 0x0F];
        }
        output = buffer + ADDRSIZE;
        for (index = lower; index < upper; index++)
        {
            *output++ = ' ';
            if (index < offset)
            {
                *output++ = ' ';
                *output++ = ' ';
            }
            else if (index < bytes)
            {
                *output++ = DIGITS_HEX[(origin[index] >> 4) & 0x0F];
                *output++ = DIGITS_HEX[(origin[index] >> 0) & 0x0F];
            }
            else
            {
                *output++ = ' ';
                *output++ = ' ';
            }
        }
        *output++ = ' ';
        for (index = lower; index < upper; index++)
        {
            if (index < offset)
            {
                *output++ = ' ';
            }
            else if (index < bytes)
            {
                unsigned c = origin[index];
                *output++ = isprint(c) ? c : '.';
            }
            else
            {
                *output++ = ' ';
            }
        }
        *output++ = '\n';
        *output++ = '\0';
        printf("%s", buffer);
        lower += block;
        upper += block;
    }
    if (bytes)
    {
        output = buffer;
        *output++ = '\n';
        *output++ = '\0';
        printf("%s", buffer);
    }
    return;
}

int main(int argc, char *argv[])
{
    mbedtls_ecdh_context ctx_bob, ctx_pub_bob;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_pk_context pk_bob;
    mbedtls_x509_crt crt_bob;

    int ret;

    mbedtls_ecdh_init(&ctx_bob);
    mbedtls_ecdh_init(&ctx_pub_bob);
    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_pk_init(&pk_bob);
    mbedtls_x509_crt_init(&crt_bob);

    // Initialize RNG and entropy source
    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)) != 0)
    {
        mbedtls_printf(" failed\n  ! mbedtls_ctr_drbg_seed returned -0x%04X\n", -ret);
        goto cleanup;
    }

    if ((ret = mbedtls_pk_parse_keyfile(&pk_bob, KEY_FILE, NULL, mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
    {
        mbedtls_printf(" failed\n  ! mbedtls_pk_parse_keyfile returned -0x%04X\n", -ret);
        goto cleanup;
    }

    mbedtls_ecp_keypair *bob_keypair = mbedtls_pk_ec(pk_bob);

    if ((ret = mbedtls_ecdh_get_params(&ctx_bob, bob_keypair, MBEDTLS_ECDH_OURS)) != 0)
    {
        mbedtls_printf(" failed\n  ! mbedtls_ecdh_get_params returned -0x%04X\n", -ret);
        goto cleanup;
    }

    if ((ret = mbedtls_ecdh_gen_public(&ctx_bob.grp, &ctx_bob.d, &ctx_bob.Q, mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
    {
        mbedtls_printf(" failed\n  ! mbedtls_ecdh_make_public returned -0x%04X\n", -ret);
        goto cleanup;
    }

    // Exchange public keys between SA and EVCC
    // In a real-world scenario, this would be done over a communication channel
    // Here we just simulate it by copying the public keys to each other's context

    // EVCC Public Key Context
    if ((ret = mbedtls_x509_crt_parse_file(&crt_bob, CRTIFICATE_FILE)) != 0)
    {
        mbedtls_printf(" failed\n  ! mbedtls_x509_crt_parse_file returned -0x%04X\n", -ret);
        goto cleanup;
    }
    mbedtls_ecp_keypair *bob_pub_keypair = mbedtls_pk_ec(crt_bob.pk);

    if ((ret = mbedtls_ecdh_get_params(&ctx_pub_bob, bob_pub_keypair, MBEDTLS_ECDH_OURS)) != 0)
    {
        mbedtls_printf("%d: failed\n  ! mbedtls_ecdh_get_params returned -0x%04X\n", __LINE__, -ret);
        goto cleanup;
    }

    printf("%d: ctx_bob public key\n", __LINE__);
    printf("%d: d\n", __LINE__);
    hexdump(&ctx_bob.d, sizeof(mbedtls_mpi));
    printf("%d: Q\n", __LINE__);
    hexdump(&ctx_bob.Q, sizeof(mbedtls_ecp_point));
    printf("%d: grp\n", __LINE__);
    hexdump(&ctx_bob.grp, sizeof(mbedtls_ecp_group));

    printf("%d: ctx_pub_bob public key\n", __LINE__);
    printf("%d: d\n", __LINE__);
    hexdump(&ctx_pub_bob.d, sizeof(mbedtls_mpi));
    printf("%d: Q\n", __LINE__);
    hexdump(&ctx_pub_bob.Q, sizeof(mbedtls_ecp_point));
    printf("%d: grp\n", __LINE__);
    hexdump(&ctx_pub_bob.grp, sizeof(mbedtls_ecp_group));

cleanup:
    mbedtls_ecdh_free(&ctx_bob);
    mbedtls_entropy_free(&entropy);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_pk_free(&pk_bob);
    mbedtls_x509_crt_free(&crt_bob);

    return 0;
}

代码中的错误分析

  • 错误1:不必要的mbedtls_ecdh_gen_public调用覆盖原有公钥
    你已经通过mbedtls_pk_parse_keyfile加载了Bob的私钥,且通过mbedtls_ecdh_get_params将密钥对导入ctx_bob,此时ctx_bob.Q已经是私钥对应的合法公钥。但后续调用mbedtls_ecdh_gen_public会重新生成随机私钥d和对应的公钥Q,直接覆盖原有密钥对,导致ctx_bob.Q变成新生成的公钥,与证书中的公钥自然不匹配。

  • 错误2:加载对方公钥时使用错误的参数
    处理从证书中提取的对方公钥时,应使用MBEDTLS_ECDH_THEIRS参数,而非MBEDTLS_ECDH_OURS。MBEDTLS_ECDH_OURS用于初始化自己的密钥对,MBEDTLS_ECDH_THEIRS才是设置外部获取的公钥的正确参数。错误的参数会导致ctx_pub_bob初始化逻辑错误,无法正确加载公钥。

  • 错误3:直接打印结构体内存无法获取真实密钥数据
    mbedtls_mpi和mbedtls_ecp_point是复杂结构体,内部包含指针指向实际存储密钥数据的缓冲区。直接用hexdump打印结构体的内存大小(如sizeof(mbedtls_mpi)),只能打印结构体的元数据(指针、长度字段等),而非实际的密钥内容。必须使用mbedtls提供的API输出密钥,比如mbedtls_ecp_point_write_binary将公钥转为二进制字节流后再打印。

  • 错误4:未初始化ctx_pub_bob的曲线组
    在调用mbedtls_ecdh_get_params前,ctx_pub_bob.grp未初始化。虽然mbedtls_ecdh_get_params可能自动初始化,但更安全的方式是先调用mbedtls_ecp_group_load加载指定曲线(MBEDTLS_ECP_DP_SECP521R1),确保两端曲线参数一致。

关键修正示例

// 修正点1:移除覆盖原有密钥对的mbedtls_ecdh_gen_public调用
// 注释或删除以下代码:
// if ((ret = mbedtls_ecdh_gen_public(&ctx_bob.grp, &ctx_bob.d, &ctx_bob.Q, mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
// {
//     mbedtls_printf(" failed\n  ! mbedtls_ecdh_make_public returned -0x%04X\n", -ret);
//     goto cleanup;
// }

// 修正点2:使用MBEDTLS_ECDH_THEIRS加载证书中的公钥
if ((ret = mbedtls_ecdh_get_params(&ctx_pub_bob, bob_pub_keypair, MBEDTLS_ECDH_THEIRS)) != 0)
{
    mbedtls_printf("%d: failed\n  ! mbedtls_ecdh_get_params returned -0x%04X\n", __LINE__, -ret);
    goto cleanup;
}

// 修正点3:正确打印公钥的函数
static void print_ec_public_key(mbedtls_ecp_group *grp, mbedtls_ecp_point *pub_key)
{
    unsigned char pub_buf[1024];
    size_t pub_len;
    int ret = mbedtls_ecp_point_write_binary(grp, pub_key, MBEDTLS_ECP_PF_UNCOMPRESSED, &pub_len, pub_buf, sizeof(pub_buf));
    if (ret != 0)
    {
        mbedtls_printf("Failed to serialize public key: -0x%04X\n", -ret);
        return;
    }

    printf("Public Key (uncompressed): ");
    for (size_t i = 0; i < pub_len; i++)
    {
        printf("%02X", pub_buf[i]);
    }
    printf("\n");
}

// 在打印部分调用该函数:
printf("ctx_bob public key:\n");
print_ec_public_key(&ctx_bob.grp, &ctx_bob.Q);

printf("ctx_pub_bob public key:\n");
print_ec_public_key(&ctx_pub_bob.grp, &ctx_pub_bob.Q);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:59:53