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

如何在Node.js中验证mbedtls生成的RSA-MD5数字签名

问题根因

你遇到的问题核心是对mbedtls_pk_verify接口的参数理解错误:
mbedtls官方定义中,mbedtls_pk_verify的第三个参数是提前计算完成的哈希值,不是待签名的原始消息,接口定义如下:

int mbedtls_pk_verify(
    mbedtls_pk_context *ctx,
    mbedtls_md_type_t md_alg,
    const unsigned char *hash, // 预先计算好的哈希值,不是原始消息
    size_t hash_len, // 哈希值长度,MD5对应固定16字节
    const unsigned char *sig,
    size_t sig_len
);

你测试用的原始消息"This is a test.."刚好是16字节长度,和MD5哈希的输出长度完全一致,所以mbedtls没有抛出长度错误,直接把你传入的原始消息当成了预先计算好的MD5哈希值,封装MD5对应的DigestInfo结构后完成了验签,属于巧合下的验证通过。
而Node.js的crypto.verify接口的标准逻辑是:传入原始消息后,会自动按照指定算法先计算原始消息的哈希值,再封装DigestInfo结构做验签,两边的处理逻辑完全不一致,所以验证失败。

修复方案

方案1:修正mbedtls验签逻辑(推荐,符合标准规范)

先对原始消息计算MD5哈希,再把哈希值传给mbedtls_pk_verify接口,修正后ESP32侧和Node.js侧的验签逻辑会完全对齐,不需要修改Node.js代码即可正常验证。
ESP32侧修正后的核心代码:

#include "mbedtls/md.h"

// 先计算原始消息的MD5哈希
unsigned char correct_message[] = "This is a test..";
unsigned char md5_hash[16];
mbedtls_md_context_t md_ctx;

mbedtls_md_init(&md_ctx);
mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_MD5), 0);
mbedtls_md_starts(&md_ctx);
mbedtls_md_update(&md_ctx, correct_message, strlen((char*)correct_message));
mbedtls_md_finish(&md_ctx, md5_hash);
mbedtls_md_free(&md_ctx);

// 传入哈希值做验签
printf("Verify result 1: %d\n",
         mbedtls_pk_verify(&pk_public, MBEDTLS_MD_MD5, md5_hash,
                           16, hard_signature,
                           hard_signature_len));

方案2:修改Node.js逻辑适配现有ESP32代码(仅临时调试用)

如果暂时不能修改ESP32侧的代码,可以在Node.js侧手动对齐现有mbedtls的错误逻辑:把原始消息直接当成MD5哈希值,构造对应的DigestInfo结构后再做验签。
Node.js侧修正后的代码:

import crypto from "crypto";

const publicKeyContent =
  "-----BEGIN PUBLIC KEY-----\n" +
  "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YbMPv02Danre1OGkkkR\n" +
  "F093SREmgsZG/o+3oDYIGm8aLESt94GGdJzd97R+usprFtnKRDoo0eICAVY1NiIi\n" +
  "m9840J9+q6MOlQJlvxQy1A3+K1Vg/XsEwRTNOR3yweC6KdVeD5ITKn4nNNBdhUdu\n" +
  "fR80DOK7i4RjNVwYAzGYiwrkcMgBVu8BVKl/P0712Gi/58yAat9qfOvn8Q6rsy3T\n" +
  "8YGS24KCN6xQPBt+bNXnMDBwxlzky7N2rfiv3Zid0R32PivBNCsNQmK+SP0zyJ2h\n" +
  "TmI6VQ4GSBObfgK4dnaDfGlHfR1RNapzpd/PfdYw4XSjDF98IQJqyd83MjTXQK+a\n" +
  "UwIDAQAB\n" +
  "-----END PUBLIC KEY-----\n";

const signature =
  "Y59Z0Jg1WCKKMpKuGYW5nLsyDgPU3eoMwtLd74lejj6bWr2lPh5MKzcfXE18s6L7YG7yB18WNW" +
  "/meh2KAegHEDZKqy2OHf0n7bKz17OSIcoSgI9N4Eu116naVtqO5vwYJ3JGqE625JkuQza4sLZh" +
  "ccosbd+mNLUEBN7sr+BtlY1rU0hed56GbP6iJ+8x5XEW4HBUQ1ATQQGNu5QtqgBA+" +
  "jv8TktXlOVJ8XXKkOUUmVu1NU1TX+hXgNqZC6drtg5+" +
  "UtaMS77eF5qctMXyNKTqJ9UGTi1S0pg3TJoVCI7A8M6GLuEsvZ0gNyDDhjY73mR2KtXknEEA4E" +
  "TfGS1xG0SE7g==";

const publicKey = crypto.createPublicKey({
  key: publicKeyContent,
  type: "spki",
  format: "pem",
});

// MD5对应的DigestInfo前缀
const md5DigestPrefix = Buffer.from([0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10]);
// 把原始消息当成MD5哈希,拼接前缀得到待对比内容
const expected = Buffer.concat([md5DigestPrefix, Buffer.from("This is a test..")]);
// 公钥解密签名得到实际内容
const decryptedSig = crypto.publicDecrypt({
  key: publicKey,
  padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(signature, "base64"));

// 对比内容是否一致
console.log("Verify result:", decryptedSig.equals(expected));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:54:03