如何用Hedera JavaScript SDK结合HSM手动签名交易?遇签名无效错误
解决Hedera交易HSM签名的INVALID_SIGNATURE问题
问题根源
你的代码存在两个关键错误导致签名无效:
- 使用
transaction.toBytes()获取待签名数据,但该方法返回的是完整交易信封字节(包含空签名字段),而Hedera要求签名的是交易体的核心字节,正确方法是transaction.toBytesToSign()。 - 直接向客户端传入私钥(
client.setOperator(operatorId, operatorPrivateKeyHex)),违反了HSM私钥不离开硬件设备的安全规则,应仅设置操作员账户ID和公钥。
正确实现流程
按照你提出的流程,适配HSM签名的代码如下:
const { Client, AccountId, PublicKey, TransferTransaction, Hbar, } = require("@hashgraph/sdk"); // 模拟HSM签名函数,实际需替换为你的HSM设备调用逻辑 async function signWithHsm(dataToSign) { // 此处调用HSM的签名接口,传入待签名字节(Buffer类型) // 返回签名结果(Buffer类型,需符合Hedera的签名格式要求) // 示例:return await hsmSdk.sign(dataToSign); } async function main() { const client = Client.forTestnet(); const operatorId = AccountId.fromString("0.0.XXX"); // 仅传入公钥,私钥全程保留在HSM中 const operatorPublicKey = PublicKey.fromString("0xXXX"); // 替换为你的公钥十六进制字符串 // 设置操作员账户ID和公钥,不传入私钥 client.setOperatorWith(operatorId, operatorPublicKey); // 1. 创建交易 let transaction = new TransferTransaction() .addHbarTransfer(operatorId, Hbar.fromTinybars(-100)) .addHbarTransfer(AccountId.fromString("0.0.3"), Hbar.fromTinybars(100)) .setNodeAccountIds([AccountId.fromString("0.0.5")]) // 2. 冻结交易,固定交易体内容(后续无法修改) .freezeWith(client); // 3. 获取需要签名的交易体核心字节 const bytesToSign = transaction.toBytesToSign(); // 调用HSM完成签名 const signature = await signWithHsm(bytesToSign); // 4. 为交易添加HSM生成的签名 transaction.addSignature(operatorPublicKey, signature); // 5. 提交已签名的交易到Hedera网络 const txResponse = await transaction.execute(client); const receipt = await txResponse.getReceipt(client); console.log("交易执行结果:", receipt); } main().catch(console.error);
关键细节说明
toBytesToSign():该方法返回的是交易体的序列化字节,这是Hedera节点验证签名时唯一认可的数据源,确保签名与交易内容严格匹配。- HSM签名适配:
signWithHsm函数需要根据你使用的HSM设备(如AWS CloudHSM、Azure Key Vault或硬件厂商SDK)实现,确保传入的字节格式和返回的签名格式符合Hedera要求(通常为ECDSA签名的原始字节或DER编码格式)。 - 安全操作规范:使用
setOperatorWith仅传递账户ID和公钥,避免私钥出现在应用内存中,完全符合HSM的安全隔离要求。
内容的提问来源于stack exchange,提问作者Pathorn Teng
相关产品推荐
相关产品推荐

