# ECDH密钥协商 ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 功能概述 ECDH(Elliptic Curve Diffie-Hellman)用于在不安全信道上协商共享密钥。通信双方分别生成椭圆曲线密钥对并交换公钥,再各自计算得到相同的共享密钥。 ECDH不直接提供数据加解密。协商出的共享密钥应通过KDF/HKDF派生出会话密钥,并配合AES-GCM或ChaCha20-Poly1305等算法保护业务数据。 ## 主要应用场景 1. TLS/DTLS安全连接:客户端和服务器使用ECDHE协商会话密钥,再用AES-GCM/ChaCha20加密数据。ECDHE每次连接生成临时密钥,即使长期证书泄露,历史会话通常仍然安全。 2. IoT设备配网与首次绑定:手机与设备首次连接时交换临时公钥,协商密钥后保护Wi-Fi密码、Token、服务器地址等敏感配置。需要结合设备证书、二维码密钥或绑定码验证身份,否则容易遭受中间人攻击。 3. 端到端加密通信:聊天、文件传输或设备间通信中,发送方和接收方使用ECDH协商密钥,使中间服务器无法读取数据。实际协议还会加入身份签名、密钥轮换和防重放机制。 4. 设备与云平台建立安全通道:设备和云端分别持有密钥对,通过ECDH派生设备专属会话密钥,用于加密遥测数据、控制命令和固件升级信息。通常还要配合设备证书或预置公钥认证云端和设备。 5. 安全启动和固件升级中的密钥封装:服务器针对设备公钥协商或派生临时加密密钥,用于保护固件、配置或授权数据。ECDH不能替代固件签名,固件真实性仍应通过数字签名验证。 ## 常见问题 1. 必须验证对端身份:ECDH本身不能防中间人攻击,应配合证书、数字签名或预共享密钥。 2. 不要直接使用协商出的共享密钥加密业务数据:应通过HKDF-SHA-256等派生函数生成加密密钥、IV等不同用途的密钥材料。 3. 校验对端公钥:检查曲线、编码、长度及公钥有效性,拒绝非法点和无穷远点。 4. 私钥必须安全随机:使用可信随机数源,不复用临时私钥,也不得将私钥输出到日志或硬编码在代码中。 5. 优先临时ECDH(ECDHE):每次会话生成新密钥,可提供前向保密。 6. 选择成熟的标准曲线:优先X25519;需要兼容既有系统时使用P-256。 7. 及时清除敏感数据:共享密钥、临时私钥和派生密钥材料使用完毕后应及时清除。 # ECDH密钥协商API ## 头文件 *mbedtls/ecdh.h* *mbedtls/ecp.h* ## 函数概览 | **函数** | **说明** | | --- | --- | | *mbedtls_ecdh_init()* | 初始化ECDH上下文 | | *mbedtls_ecdh_setup()* | 配置ECDH上下文使用的椭圆曲线组 | | *mbedtls_ecdh_make_public()* | 生成本端密钥对并导出公钥 | | *mbedtls_ecdh_read_public()* | 导入并校验对端公钥 | | *mbedtls_ecdh_calc_secret()* | 计算并导出双方共享密钥 | | *mbedtls_ecdh_free()* | 清除敏感数据并释放ECDH上下文 | | *mbedtls_ecdh_gen_public()* | 在指定曲线组上生成ECDH密钥对 | | *mbedtls_ecdh_compute_shared()* | 计算ECDH共享密钥 | | *mbedtls_ecdh_can_do()* | 检查曲线是否支持ECDH | | *mbedtls_ecdh_get_params()* | 从EC密钥导入ECDH参数 | ## 函数详解 ### mbedtls_ecdh_init() - **功能描述** 初始化ECDH上下文。 - **函数原型** ```c void mbedtls_ecdh_init(mbedtls_ecdh_context *ctx); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输出 | *mbedtls_ecdh_context** | ECDH上下文;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | - **返回值说明** 无 ### mbedtls_ecdh_setup() - **功能描述** 配置ECDH上下文使用的椭圆曲线组。 - **函数原型** ```c int mbedtls_ecdh_setup(mbedtls_ecdh_context *ctx, mbedtls_ecp_group_id grp_id); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输入/输出 | *mbedtls_ecdh_context** | 已初始化的ECDH上下文;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | | *grp_id* | 输入 | mbedtls_ecp_group_id | 椭圆曲线标识;常用 *MBEDTLS_ECP_DP_SECP256R1* | - **返回值说明** *0*:函数执行成功 其他值:函数执行失败 ### mbedtls_ecdh_make_public() - **功能描述** 生成本端密钥对并导出公钥。 - **函数原型** ```c int mbedtls_ecdh_make_public(mbedtls_ecdh_context *ctx, size_t *olen, unsigned char *buf, size_t blen, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输入/输出 | *mbedtls_ecdh_context** | 已初始化并完成曲线配置的ECDH上下文;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | | *olen* | 输出 | size_t * | 导出的公钥长度;单位:字节 | | *buf* | 输出 | unsigned char * | 保存本端公钥的缓冲区 | | *blen* | 输入 | size_t | *buf* 缓冲区长度;单位:字节 | | *f_rng* | 输入 | int (*)(void *, unsigned char *, size_t) | 随机数生成函数;不能为NULL | | *p_rng* | 输入 | void * | 随机数生成上下文;可为NULL | - **返回值说明** *0*:函数执行成功 *MBEDTLS_ERR_ECP_IN_PROGRESS*:启用了可恢复椭圆曲线计算,当前计算尚未完成 其他值:函数执行失败 ### mbedtls_ecdh_read_public() - **功能描述** 导入并校验对端公钥。 - **函数原型** ```c int mbedtls_ecdh_read_public(mbedtls_ecdh_context *ctx, const unsigned char *buf, size_t blen); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输入/输出 | *mbedtls_ecdh_context** | 已生成本端密钥对的ECDH上下文;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | | *buf* | 输入 | const unsigned char * | 对端发送的公钥数据 | | *blen* | 输入 | size_t | 对端公钥数据长度;单位:字节 | - **返回值说明** *0*:函数执行成功 其他值:函数执行失败 ### mbedtls_ecdh_calc_secret() - **功能描述** 计算并导出双方共享密钥。 - **函数原型** ```c int mbedtls_ecdh_calc_secret(mbedtls_ecdh_context *ctx, size_t *olen, unsigned char *buf, size_t blen, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输入/输出 | *mbedtls_ecdh_context** | 已包含本端私钥和对端公钥的ECDH上下文;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | | *olen* | 输出 | size_t * | 共享密钥长度;单位:字节 | | *buf* | 输出 | unsigned char * | 保存共享密钥的缓冲区 | | *blen* | 输入 | size_t | *buf* 缓冲区长度;单位:字节 | | *f_rng* | 输入 | int (*)(void *, unsigned char *, size_t) | 用于侧信道防护的随机数生成函数;建议不为NULL | | *p_rng* | 输入 | void * | 随机数生成上下文;可为NULL | - **返回值说明** *0*:函数执行成功 *MBEDTLS_ERR_ECP_IN_PROGRESS*:当前计算尚未完成 其他值:函数执行失败 ### mbedtls_ecdh_free() - **功能描述** 清除敏感数据并释放ECDH上下文。 - **函数原型** ```c void mbedtls_ecdh_free(mbedtls_ecdh_context *ctx); ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ctx* | 输入/输出 | *mbedtls_ecdh_context** | 已初始化的ECDH上下文;可为NULL;详见 [*mbedtls_ecdh_context*](#mbedtlsecdhcontext) | - **返回值说明** 无 ## 结构体定义 ### mbedtls_ecdh_context ECDH上下文结构体定义如下: ```c typedef struct mbedtls_ecdh_context { mbedtls_ecp_group grp; mbedtls_mpi d; mbedtls_ecp_point Q; mbedtls_ecp_point Qp; mbedtls_mpi z; } mbedtls_ecdh_context; ``` | **参数** | **类型** | **说明** | | --- | --- | --- | | *grp* | *mbedtls_ecp_group* | 保存当前ECDH使用的椭圆曲线域参数 | | *d* | *mbedtls_mpi* | 保存本端生成的私钥 | | *Q* | *mbedtls_ecp_point* | 保存本端根据私钥生成的公钥 | | *Qp* | *mbedtls_ecp_point* | 保存并校验导入的对端公钥 | | *z* | *mbedtls_mpi* | 保存ECDH计算得到的共享秘密 | ```{note} 业务代码不应直接访问其内部成员。 ``` ## 枚举定义 ### mbedtls_ecdh_side EC密钥归属枚举定义如下: ```c typedef enum { MBEDTLS_ECDH_OURS, MBEDTLS_ECDH_THEIRS, } mbedtls_ecdh_side; ``` | **成员** | **说明** | | --- | --- | | *MBEDTLS_ECDH_OURS* | 导入本端EC密钥 | | *MBEDTLS_ECDH_THEIRS* | 导入对端EC公钥 | ### mbedtls_ecp_group_id 椭圆曲线类型枚举定义如下: ```c typedef enum { MBEDTLS_ECP_DP_NONE = 0, MBEDTLS_ECP_DP_SECP192R1, MBEDTLS_ECP_DP_SECP224R1, MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ECP_DP_SECP384R1, MBEDTLS_ECP_DP_SECP521R1, MBEDTLS_ECP_DP_BP256R1, MBEDTLS_ECP_DP_BP384R1, MBEDTLS_ECP_DP_BP512R1, MBEDTLS_ECP_DP_CURVE25519, MBEDTLS_ECP_DP_SECP192K1, MBEDTLS_ECP_DP_SECP224K1, MBEDTLS_ECP_DP_SECP256K1, MBEDTLS_ECP_DP_CURVE448, } mbedtls_ecp_group_id; ``` | **成员** | **说明** | | --- | --- | | *MBEDTLS_ECP_DP_NONE* | 未指定椭圆曲线 | | *MBEDTLS_ECP_DP_SECP192R1* | NIST P-192,不推荐新设计使用 | | *MBEDTLS_ECP_DP_SECP224R1* | NIST P-224 | | *MBEDTLS_ECP_DP_SECP256R1* | NIST P-256,推荐通用ECDH场景使用 | | *MBEDTLS_ECP_DP_SECP384R1* | NIST P-384,安全强度更高 | | *MBEDTLS_ECP_DP_SECP521R1* | NIST P-521 | | *MBEDTLS_ECP_DP_BP256R1* | Brainpool P-256 | | *MBEDTLS_ECP_DP_BP384R1* | Brainpool P-384 | | *MBEDTLS_ECP_DP_BP512R1* | Brainpool P-512 | | *MBEDTLS_ECP_DP_CURVE25519* | Curve25519,通常用于X25519密钥协商 | | *MBEDTLS_ECP_DP_SECP192K1* | 192位Koblitz曲线 | | *MBEDTLS_ECP_DP_SECP224K1* | 224位Koblitz曲线 | | *MBEDTLS_ECP_DP_SECP256K1* | secp256k1,常见于区块链 | | *MBEDTLS_ECP_DP_CURVE448* | Curve448,通常用于X448密钥协商 | ## 错误码 | **错误码** | **说明** | | --- | --- | | *MBEDTLS_ERR_ECP_BAD_INPUT_DATA* | 输入参数错误 | | *MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL* | 输出缓冲区不足 | | *MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE* | 当前曲线或功能未启用 | | *MBEDTLS_ERR_ECP_INVALID_KEY* | 对端公钥或本端密钥无效 | | *MBEDTLS_ERR_ECP_RANDOM_FAILED* | 随机数生成失败 | | *MBEDTLS_ERR_ECP_ALLOC_FAILED* | 内存分配失败 | | *MBEDTLS_ERR_ECP_VERIFY_FAILED* | 椭圆曲线验证失败 | ## 常用曲线 | **枚举值** | **曲线** | **说明** | | --- | --- | --- | | *MBEDTLS_ECP_DP_SECP256R1* | NIST P-256 | 推荐,兼容性较好 | | *MBEDTLS_ECP_DP_SECP384R1* | NIST P-384 | 更高安全强度,开销更大 | | *MBEDTLS_ECP_DP_CURVE25519* | Curve25519 | 适合ECDH密钥协商 | | *MBEDTLS_ECP_DP_SECP256K1* | secp256k1 | 主要用于区块链,不建议作为默认曲线 | # 应用逻辑流程图 ```{image} images/image_KFTAbsvC8oIICxxIuW0cyTotn9b.webp :width: 1041px :height: 737px :align: center ``` # 示例代码 完整示例代码请查看https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/crypto/ecdh_demo.c