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

