如何在ESP-IDF中从ATECC608B获取64字节ECDSA签名
尝试从buf中获取ATECC608B签名后的64字节ECDSA签名,但返回的大小为72字节,有时会根据hash的大小在70-72字节间变化。签名与验证功能可正常运行,但无法提取出64字节的签名。
相关签名生成代码:
ESP_LOGI(TAG, " Generating ECDSA Signature..."); ret = mbedtls_pk_sign(&pkey, MBEDTLS_MD_SHA256, hash, 0, buf, MBEDTLS_MPI_MAX_SIZE, &olen, mbedtls_ctr_drbg_random, &ctr_drbg); if (ret != 0) { ESP_LOGI(TAG, " failed ! mbedtls_pk_sign returned -0x%04x", -ret); goto exit; } ESP_LOGI(TAG, " ok");
签名打印代码:
ESP_LOGI(TAG, "Signature:"); ESP_LOGW(TAG, "%i", olen); ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, olen, ESP_LOG_WARN);
日志输出:
I (649) atecc_example: Signature:
W (649) atecc_example: 72
W (649) atecc_example: 30 46 02 21 00 f6 8b 21 9f 60 47 d3 f7 99 e0 a4
W (649) atecc_example: a1 0b df 4d d3 78 36 3e d1 0c 2c 34 4c 14 39 6d
W (659) atecc_example: 2f 5c cb 43 bc 02 21 00 bd 20 87 ed fa fa 81 db
W (669) atecc_example: 02 df 82 0e 3c 3d 5a 35 c3 6b c5 0d cb 35 d2 e1
W (679) atecc_example: 7b 3a 3a ca 21 59 6b cc
mbedtls_pk_sign返回的是ASN.1 DER编码格式的ECDSA签名,不是原始的64字节(r,s)拼接格式。DER编码的结构如下:
- 0x30:SEQUENCE类型标签
- 后续字节:整个签名的总长度
- 0x02:INTEGER类型标签(对应签名的r值)
- 后续字节:r值的字节长度(31或33字节,当r最高位为1时,会添加0x00前缀避免被解析为负数)
- r的实际字节数据
- 0x02:INTEGER类型标签(对应签名的s值)
- 后续字节:s值的字节长度
- s的实际字节数据
这就是签名长度在70-72字节波动的原因:当r或s的最高位为0时,会省略前缀0x00,整体长度减少2字节。
方案1:使用mbedtls API解析
通过mbedtls提供的接口直接提取r和s值,再拼接成64字节原始签名:
#include "mbedtls/ecdsa.h" // 初始化上下文 mbedtls_ecdsa_context ecdsa_ctx; mbedtls_ecdsa_init(&ecdsa_ctx); mbedtls_mpi r, s; mbedtls_mpi_init(&r); mbedtls_mpi_init(&s); // 从PK上下文导入密钥信息 if(mbedtls_ecdsa_from_pk(&ecdsa_ctx, &pkey) != 0) { // 处理错误逻辑 } // 解析DER格式签名到r和s if(mbedtls_ecdsa_read_signature(&ecdsa_ctx, buf, olen, &r, &s) != 0) { // 处理错误逻辑 } uint8_t raw_signature[64] = {0}; // 将r转为32字节大端格式,填充到前32位 mbedtls_mpi_write_binary(&r, raw_signature, 32); // 将s转为32字节大端格式,填充到后32位 mbedtls_mpi_write_binary(&s, raw_signature + 32, 32); // 清理资源 mbedtls_mpi_free(&r); mbedtls_mpi_free(&s); mbedtls_ecdsa_free(&ecdsa_ctx);
方案2:手动解析DER结构
根据ASN.1 DER规则手动提取r和s的有效数据:
- 跳过开头的
0x30 xx(SEQUENCE标签和长度) - 读取
0x02 xx(r的INTEGER标签和长度):- 如果长度是33,跳过第一个字节(0x00前缀),取后面32字节作为r
- 如果长度是31,直接取31字节,前面补1个0x00凑成32字节
- 读取
0x02 xx(s的INTEGER标签和长度),按同样规则提取32字节的s - 将r和s拼接得到64字节原始签名
内容的提问来源于stack exchange,提问作者damo

