使用mbedtls无法匹配证书/私钥公钥到mbedtls_ecdh_context问题排查
基于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

