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

Hyperledger Fabric离线签名实现报错:access denied问题排查

问题描述

使用fabric-gateway包实现Hyperledger Fabric离线签名时,交易背书失败,返回"access denied"错误,peer日志明确提示提案签名无效。使用内置Signer方法时可正常运行,但离线签名始终失败。

代码片段

async function main() {
  const identityPath = path.join(__dirname, 'wallet/INMETROMSP/', 'te.id');
  const identityData = JSON.parse(await fs.readFileSync(identityPath, 'utf8'));
  const certificate = utf8Encoder.encode(identityData.credentials.certificate);

  const privateKeyPath = path.resolve(__dirname, 'private_key.pem');
  const privateKeyPem = fs.readFileSync(privateKeyPath, 'utf8');
  const privateKey = crypto.createPrivateKey(privateKeyPem);

  // const signer = signers.newPrivateKeySigner(privateKey);

  const configContent = fs.readFileSync('connection-org.yaml','utf-8');
  const data = yaml.load(configContent);
  const cert = data.peers['inmetro-peer0.default'].tlsCACerts.pem;
  const tlsRootCert = Buffer.from(cert);

  const client = new grpc.Client('peer0.inmetro.br:443', grpc.credentials.createSsl(tlsRootCert));
  const noOpHash = (message) => message;

  const gateway = connect({
    identity: { 
      mspId: MSPID,
      credentials: certificate,
     },
    // hash: noOpHash,
    // signer,
    client,
  });

  try {
    const network = gateway.getNetwork(CHANNEL);
    const contract = network.getContract(CC_NAME);

    const vehiclePlate = "ABC1234"
    const reportData = JSON.stringify({
      data: [4199, 4179, 2923, 2815, 3453, 3120, 1962, 1452, 3384],
    });
    
    const unsignedProposal = contract.newProposal('RegisterMeter', vehiclePlate, reportData);
    const proposalBytes = unsignedProposal.getBytes();
    const proposalDigest = unsignedProposal.getDigest();
    const proposalSignature = crypto.sign('sha256', proposalDigest, privateKey);
    const signedProposal = gateway.newSignedProposal(proposalBytes, proposalSignature);

    const unsignedTransaction = await signedProposal.endorse();
    const transactionBytes = unsignedTransaction.getBytes();
    const transactionDigest = unsignedTransaction.getDigest();
    const transactionSignature = signDigest(unsignedTransaction.getDigest());
    const signedTransaction = gateway.newSignedTransaction(transactionBytes, transactionSignature);

    const unsignedCommit = await signedTransaction.submit();
    const commitBytes = unsignedCommit.getBytes();
    const commitDigest = unsignedCommit.getDigest();
    const commitSignature = signDigest(unsignedCommit.getDigest());
    const signedCommit = gateway.newSignedCommit(commitBytes, commitSignature);

    const result = signedTransaction.getResult();
    const status = await signedCommit.getStatus();

    console.log(result,status);
}

错误信息

客户端错误

EndorseError: 10 ABORTED: failed to endorse transaction, see attached details for more info
    at /home/edu/fabric-node-webserver/client/braketester/node_modules/@hyperledger/fabric-gateway/dist/client.js:37:253
    at Object.callback (/home/edu/fabric-node-webserver/client/braketester/node_modules/@hyperledger/fabric-gateway/dist/client.js:102:20)
    at Object.onReceiveStatus (/home/edu/fabric-node-webserver/client/braketester/node_modules/@grpc/grpc-js/build/src/client.js:192:36)
    ... 2 lines matching cause stack trace ...
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  code: 10,
  details: [
    {
      address: 'peer0.inmetro.br:443',
      message: 'error validating proposal: access denied: channel [demo] creator org [INMETROMSP]',
      mspId: 'INMETROMSP'
    }
  ],
  cause: Error: 10 ABORTED: failed to endorse transaction, see attached details for more info
      at callErrorFromStatus (/home/edu/fabric-node-webserver/client/braketester/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
      at Object.onReceiveStatus (/home/edu/fabric-node-webserver/client/braketester/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
      ...
  transactionId: '9276e29c9c4e29e40eca631ad3c568415ec345cd2952c05aee5ce814d0d33a3d'
}

Peer日志

2025-05-09 18:14:17.377 UTC 02ca WARN [endorser] Validate -> access denied: creator's signature over the proposal is not valid channel=demo txID=9276e29c mspID=INMETROMSP error="The signature is invalid" errorVerbose="The signature is invalid\ngithub.com/hyperledger/fabric/msp.(*identity).Verify\n\t/msp/identities.go:202\ngithub.com/hyperledger/fabric/core/endorser.(*UnpackedProposal).Validate\n\t/core/endorser/msgvalidation.go:189\ngithub.com/hyperledger/fabric/core/endorser.(*Endorser).preProcess\n\t/core/endorser/endorser.go:258\ngithub.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposal\n\t/core/endorser/endorser.go:335\ngithub.com/hyperledger/fabric/core/handlers/auth/filter.(*expirationCheckFilter).ProcessProposal\n\t/core/handlers/auth/filter/expiration.go:61\ngithub.com/hyperledger/fabric/core/handlers/auth/filter.(*filter).ProcessProposal\n\t/core/handlers/auth/filter/filter.go:32\ngithub.com/hyperledger/fabric/internal/pkg/gateway.(*EndorserServerAdapter).ProcessProposal\n\t/internal/pkg/gateway/gateway.go:43\ngithub.com/hyperledger/fabric/internal/pkg/gateway.(*Server).planFromFirstEndorser.func1\n\t/internal/pkg/gateway/endorse.go:205\nruntime.goexit\n\t/usr/local/go/src/runtime/asm_amd64.s:1700" identity="(mspid=INMETROMSP subject=CN=te,OU=client,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU issuer=CN=ca,OU=Tech,O=Kung Fu Software,STREET=Alicante,L=Alicante,C=ES serialnumber=723095516253312706461993611318417152021379977104)"
2025-05-09 18:14:17.377 UTC 02cb WARN [endorser] ProcessProposal -> Failed to preProcess proposal error="error validating proposal: access denied: channel [demo] creator org [INMETROMSP]"
2025-05-09 18:14:17.377 UTC 02cc WARN [gateway] func1 -> Endorse call to endorser failed channel=demo chaincode=braketester-external txID=9276e29c9c4e29e40eca631ad3c568415ec345cd2952c05aee5ce814d0d33a3d endorserAddress=peer0.inmetro.br:443 endorserMspid=INMETROMSP error="error validating proposal: access denied: channel [demo] creator org [INMETROMSP]"

解决方案

Peer日志明确指出提案签名无效,结合代码分析,需修复以下几个核心问题:

1. 配置正确的哈希函数

离线签名时,Gateway需要使用与Fabric网络一致的SHA256哈希算法生成提案摘要,你注释了hash配置导致摘要逻辑错误,需添加:

const hash = (message) => crypto.createHash('sha256').update(message).digest();

并在Gateway连接时启用:

const gateway = connect({
  identity: { 
    mspId: MSPID,
    credentials: certificate,
  },
  hash: hash, // 启用SHA256哈希函数
  client,
});

2. 实现缺失的signDigest函数

代码中调用了未定义的signDigest函数,需实现与提案签名一致的逻辑,同时适配密钥类型:

function signDigest(digest) {
  // 根据密钥类型选择对应签名算法
  const algorithm = privateKey.asymmetricKeyType === 'ec' ? 'ecdsa-with-SHA256' : 'sha256';
  return crypto.sign(algorithm, digest, privateKey);
}

3. 确保签名算法与密钥类型匹配

Node.js crypto.sign的算法参数必须与私钥类型匹配:

  • RSA密钥:使用'sha256'
  • EC密钥:使用'ecdsa-with-SHA256'
    可通过以下代码确认密钥类型:
console.log('私钥类型:', privateKey.asymmetricKeyType);

4. 验证证书格式

确保证书是PEM格式的UTF-8编码字节,可替换原证书处理代码为:

const certificate = Buffer.from(identityData.credentials.certificate, 'utf8');

修复后的关键代码片段

// 配置SHA256哈希函数
const hash = (message) => crypto.createHash('sha256').update(message).digest();

// 实现通用签名函数
function signDigest(digest) {
  const algorithm = privateKey.asymmetricKeyType === 'ec' ? 'ecdsa-with-SHA256' : 'sha256';
  return crypto.sign(algorithm, digest, privateKey);
}

// 初始化Gateway
const gateway = connect({
  identity: { 
    mspId: MSPID,
    credentials: Buffer.from(identityData.credentials.certificate, 'utf8'),
  },
  hash: hash,
  client,
});

// 提案签名
const proposalSignature = signDigest(unsignedProposal.getDigest());
const signedProposal = gateway.newSignedProposal(proposalBytes, proposalSignature);

// 交易签名
const transactionSignature = signDigest(unsignedTransaction.getDigest());
const signedTransaction = gateway.newSignedTransaction(transactionBytes, transactionSignature);

// 提交签名
const commitSignature = signDigest(unsignedCommit.getDigest());
const signedCommit = gateway.newSignedCommit(commitBytes, commitSignature);

内容的提问来源于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 04:07:03