如何在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
相关产品推荐
相关产品推荐

