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

Hyperledger Fabric Node.js客户端规范签名问题求助

解决Fabric离线签名的规范Low-S签名问题

核心原因

Fabric要求ECDSA签名必须是规范形式(Low-S),即签名中的S值必须≤曲线阶的一半。Node.js原生crypto.sign方法默认不强制生成Low-S签名,导致peer验证时抛出S值过高的错误。

可行解决方案

方案1:使用@noble/curves包(推荐)

@noble/curves的p256(对应prime256v1曲线)模块原生支持生成Low-S签名,完全适配Fabric的签名要求。

代码示例:

import { p256 } from '@noble/curves/p256';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';

// 将PEM格式私钥转换为裸私钥字节数组
function pemToPrivateKeyBytes(pem) {
  const pemHeader = '-----BEGIN PRIVATE KEY-----';
  const pemFooter = '-----END PRIVATE KEY-----';
  const pemContent = pem.replace(pemHeader, '').replace(pemFooter, '').replace(/\s/g, '');
  return Uint8Array.from(Buffer.from(pemContent, 'base64'));
}

// 对交易摘要生成规范Low-S签名
function signProposalDigest(proposalDigest, privateKeyPem) {
  const privateKeyBytes = pemToPrivateKeyBytes(privateKeyPem);
  // 默认生成符合要求的Low-S签名
  const signature = p256.sign(proposalDigest, privateKeyBytes);
  // 转换为Fabric兼容的DER格式
  return signature.toDER();
}

方案2:手动调整原生crypto生成的签名

如果坚持使用Node.js原生crypto模块,需要手动解析签名的R、S值,将超出阈值的S值替换为曲线阶 - S,再重新编码为DER格式。

代码示例:

const crypto = require('crypto');
const asn1 = require('asn1.js'); // 需先安装asn1.js包

// 定义ECDSA签名的ASN.1结构,用于解析/编码签名
const EcdsaSig = asn1.define('EcdsaSig', function() {
  this.seq().obj(
    this.key('r').int(),
    this.key('s').int()
  );
});

function normalizeSignature(signature, curve) {
  const ecdh = crypto.createECDH(curve);
  const n = ecdh.getPrivateKey().asn1Curve.n; // 获取曲线阶
  // 解析DER格式签名为R、S值
  const decoded = EcdsaSig.decode(signature, 'der');
  
  // 如果S大于曲线阶的一半,替换为n - S
  if (decoded.s.compare(n.divn(2)) === 1) {
    decoded.s = n.sub(decoded.s);
  }
  
  // 重新编码为DER格式
  return EcdsaSig.encode(decoded, 'der');
}

// 原生签名后执行规范化处理
function signWithNormalization(proposalDigest, privateKeyPem) {
  const privateKey = crypto.createPrivateKey(privateKeyPem);
  // 生成原始签名
  const rawSignature = crypto.sign('sha256', proposalDigest, privateKey);
  // 转换为规范Low-S签名
  return normalizeSignature(rawSignature, 'prime256v1');
}

集成到fabric-gateway离线签名流程

在fabric-gateway的签名回调中使用上述生成的规范签名即可:

const { Gateway, Wallets } = require('fabric-network');

async function submitOfflineSignedTransaction() {
  const gateway = new Gateway();
  const wallet = await Wallets.newFileSystemWallet('./wallet');
  
  await gateway.connect(ccp, {
    wallet,
    identity: 'your-identity',
    signer: async (digest) => {
      const identity = await wallet.get('your-identity');
      const normalizedSignature = signProposalDigest(digest, identity.privateKey);
      return { signature: normalizedSignature };
    }
  });
  
  // 后续交易提交流程...
}

注意事项

  • 确保使用prime256v1曲线(Fabric默认曲线),不要混淆为secp256k1。
  • 手动调整S值时必须使用大数运算,避免直接操作字节流导致的格式错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:21:08